SQL: Introduce IsNull node to simplify expressions (#35206)
Add `IsNull` node in parser to simplify expressions so that `<value> IS NULL` is no longer translated internally to `NOT(<value> IS NOT NULL)` Replace `IsNotNullProcessor` with `CheckNullProcessor` to encapsulate both isNull and isNotNull functionality. Closes: #34876 Fixes: #35171
This commit is contained in:
parent
529910a43c
commit
36da6e1671
|
@ -64,10 +64,24 @@ SELECT last_name l FROM "test_emp" WHERE emp_no < 10003 AND (gender = 'M' AND NO
|
|||
|
||||
// TODO: (NOT) RLIKE in particular and more NOT queries in general
|
||||
|
||||
whereIsNotNullAndComparison
|
||||
SELECT last_name l FROM "test_emp" WHERE emp_no IS NOT NULL AND emp_no < 10005 ORDER BY emp_no;
|
||||
whereIsNull
|
||||
SELECT last_name l FROM "test_emp" WHERE emp_no IS NULL;
|
||||
whereIsNullAndNegation
|
||||
SELECT last_name l FROM "test_emp" WHERE NOT emp_no IS NULL;
|
||||
whereIsNotNull
|
||||
SELECT last_name l FROM "test_emp" WHERE emp_no IS NOT NULL;
|
||||
whereIsNotNullAndNegation
|
||||
SELECT last_name l FROM "test_emp" WHERE NOT emp_no IS NOT NULL;
|
||||
|
||||
whereIsNullAndComparison
|
||||
SELECT last_name l FROM "test_emp" WHERE emp_no IS NULL AND emp_no < 10005 ORDER BY emp_no;
|
||||
whereIsNullWithComparisonAndNegation
|
||||
SELECT last_name l FROM "test_emp" WHERE (NOT emp_no IS NULL) AND emp_no < 10005 ORDER BY emp_no;
|
||||
whereIsNotNullAndComparison
|
||||
SELECT last_name l FROM "test_emp" WHERE emp_no IS NOT NULL AND emp_no < 10005 ORDER BY emp_no;
|
||||
whereIsNotNullWithComparisonAndNegation
|
||||
SELECT last_name l FROM "test_emp" WHERE (NOT emp_no IS NOT NULL) AND emp_no < 10005 ORDER BY emp_no;
|
||||
|
||||
whereIsNotNullAndIsNull
|
||||
// tag::whereIsNotNullAndIsNull
|
||||
SELECT last_name l FROM "test_emp" WHERE emp_no IS NOT NULL AND gender IS NULL;
|
||||
|
|
|
@ -72,3 +72,24 @@ castOnColumnNumberToDouble
|
|||
SELECT CAST(emp_no AS DOUBLE) AS emp_no_cast FROM "test_emp" ORDER BY emp_no LIMIT 5;
|
||||
castOnColumnNumberToBoolean
|
||||
SELECT CAST(emp_no AS BOOL) AS emp_no_cast FROM "test_emp" ORDER BY emp_no LIMIT 5;
|
||||
|
||||
//
|
||||
// SELECT with IS NULL and IS NOT NULL
|
||||
isNullAndIsNotNull
|
||||
SELECT null IS NULL AS col1, null IS NOT NULL AS col2;
|
||||
isNullAndIsNotNullAndNegation
|
||||
SELECT NOT(null IS NULL) AS col1, NOT(null IS NOT NULL) AS col2;
|
||||
isNullAndIsNotNullOverComparison
|
||||
SELECT (null = 1) IS NULL AS col1, (null = 1) IS NOT NULL AS col2;
|
||||
isNullAndIsNotNullOverComparisonWithNegation
|
||||
SELECT NOT((null = 1) IS NULL) AS col1, NOT((null = 1) IS NOT NULL) AS col2;
|
||||
|
||||
// with table columns
|
||||
isNullAndIsNotNull_onTableColumns
|
||||
SELECT languages IS NULL AS col1, languages IS NOT NULL AS col2 FROM "test_emp" WHERE emp_no IN (10018, 10019, 10020) ORDER BY emp_no;
|
||||
isNullAndIsNotNullAndNegation_onTableColumns
|
||||
SELECT NOT languages IS NULL AS col1, NOT(languages IS NOT NULL) AS col2 FROM test_emp WHERE emp_no IN (10018, 10019, 10020) ORDER BY emp_no;
|
||||
isNullAndIsNotNullOverComparison_onTableColumns
|
||||
SELECT (languages = 2) IS NULL AS col1, (languages = 2) IS NOT NULL AS col2 FROM test_emp WHERE emp_no IN (10018, 10019, 10020) ORDER BY emp_no;
|
||||
isNullAndIsNotNullOverComparisonWithNegation_onTableColumns
|
||||
SELECT NOT((languages = 2) IS NULL) AS col1, NOT((languages = 2) IS NOT NULL) AS col2 FROM test_emp WHERE emp_no IN (10018, 10019, 10020) ORDER BY emp_no;
|
||||
|
|
|
@ -72,12 +72,12 @@ public final class Expressions {
|
|||
|
||||
public static boolean nullable(List<? extends Expression> exps) {
|
||||
for (Expression exp : exps) {
|
||||
if (!exp.nullable()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (exp.nullable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean foldable(List<? extends Expression> exps) {
|
||||
for (Expression exp : exps) {
|
||||
|
|
|
@ -28,7 +28,7 @@ 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.logical.BinaryLogicProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.NotProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNotNullProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.CheckNullProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.UnaryArithmeticProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor;
|
||||
|
@ -59,8 +59,8 @@ public final class Processors {
|
|||
entries.add(new Entry(Processor.class, BinaryLogicProcessor.NAME, BinaryLogicProcessor::new));
|
||||
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, IsNotNullProcessor.NAME, IsNotNullProcessor::new));
|
||||
|
||||
// arithmetic
|
||||
entries.add(new Entry(Processor.class, BinaryArithmeticProcessor.NAME, BinaryArithmeticProcessor::new));
|
||||
|
|
|
@ -21,10 +21,10 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.string.LocateFunct
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.ReplaceFunctionProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.StringProcessor.StringOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.SubstringFunctionProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.CoalesceProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.NotProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNotNullProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.CheckNullProcessor.CheckNullOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.BinaryArithmeticProcessor.BinaryArithmeticOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.UnaryArithmeticProcessor.UnaryArithmeticOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
|
@ -117,8 +117,12 @@ public final class InternalSqlScriptUtils {
|
|||
return NotProcessor.apply(expression);
|
||||
}
|
||||
|
||||
public static Boolean notNull(Object expression) {
|
||||
return IsNotNullProcessor.apply(expression);
|
||||
public static Boolean isNull(Object expression) {
|
||||
return CheckNullOperation.IS_NULL.apply(expression);
|
||||
}
|
||||
|
||||
public static Boolean isNotNull(Object expression) {
|
||||
return CheckNullOperation.IS_NOT_NULL.apply(expression);
|
||||
}
|
||||
|
||||
public static Boolean in(Object value, List<Object> values) {
|
||||
|
|
|
@ -16,10 +16,6 @@ import org.elasticsearch.xpack.sql.tree.Location;
|
|||
*/
|
||||
public abstract class BinaryOperator<T, U, R, F extends PredicateBiFunction<T, U, R>> extends BinaryPredicate<T, U, R, F> {
|
||||
|
||||
public interface Negateable {
|
||||
BinaryOperator<?, ?, ?, ?> negate();
|
||||
}
|
||||
|
||||
protected BinaryOperator(Location location, Expression left, Expression right, F function) {
|
||||
super(location, left, right, function);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
|
||||
public interface Negatable<T extends ScalarFunction> {
|
||||
|
||||
T negate();
|
||||
|
||||
}
|
|
@ -62,6 +62,11 @@ 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();
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.logical;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class And extends BinaryLogic implements Negateable {
|
||||
public class And extends BinaryLogic implements Negatable<BinaryLogic> {
|
||||
|
||||
public And(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryLogicOperation.AND);
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
@ -58,8 +58,8 @@ public class Not extends UnaryScalarFunction {
|
|||
@Override
|
||||
protected Expression canonicalize() {
|
||||
Expression canonicalChild = field().canonical();
|
||||
if (canonicalChild instanceof Negateable) {
|
||||
return ((Negateable) canonicalChild).negate();
|
||||
if (canonicalChild instanceof Negatable) {
|
||||
return ((Negatable) canonicalChild).negate();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.logical;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class Or extends BinaryLogic implements Negateable {
|
||||
public class Or extends BinaryLogic implements Negatable<BinaryLogic> {
|
||||
|
||||
public Or(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryLogicOperation.OR);
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.nulls;
|
||||
|
||||
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.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CheckNullProcessor implements Processor {
|
||||
|
||||
public enum CheckNullOperation implements Function<Object, Boolean> {
|
||||
|
||||
IS_NULL(Objects::isNull, "IS NULL"),
|
||||
IS_NOT_NULL(Objects::nonNull, "IS NOT NULL");
|
||||
|
||||
private final Function<Object, Boolean> process;
|
||||
private final String symbol;
|
||||
|
||||
CheckNullOperation(Function<Object, Boolean> process, String symbol) {
|
||||
this.process = process;
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
public String symbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean apply(Object o) {
|
||||
return process.apply(o);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String NAME = "nckn";
|
||||
|
||||
private final CheckNullOperation operation;
|
||||
|
||||
CheckNullProcessor(CheckNullOperation operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
public CheckNullProcessor(StreamInput in) throws IOException {
|
||||
this(in.readEnum(CheckNullOperation.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeEnum(operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
return operation.apply(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
CheckNullProcessor that = (CheckNullProcessor) o;
|
||||
return operation == that.operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(operation);
|
||||
}
|
||||
}
|
|
@ -9,12 +9,14 @@ import org.elasticsearch.xpack.sql.expression.Expression;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.CheckNullProcessor.CheckNullOperation;
|
||||
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;
|
||||
|
||||
public class IsNotNull extends UnaryScalarFunction {
|
||||
public class IsNotNull extends UnaryScalarFunction implements Negatable<UnaryScalarFunction> {
|
||||
|
||||
public IsNotNull(Location location, Expression field) {
|
||||
super(location, field);
|
||||
|
@ -37,12 +39,12 @@ public class IsNotNull extends UnaryScalarFunction {
|
|||
|
||||
@Override
|
||||
protected Processor makeProcessor() {
|
||||
return IsNotNullProcessor.INSTANCE;
|
||||
return new CheckNullProcessor(CheckNullOperation.IS_NOT_NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processScript(String script) {
|
||||
return Scripts.formatTemplate(Scripts.SQL_SCRIPTS + ".notNull(" + script + ")");
|
||||
return Scripts.formatTemplate(Scripts.SQL_SCRIPTS + ".isNotNull(" + script + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,4 +56,9 @@ public class IsNotNull extends UnaryScalarFunction {
|
|||
public DataType dataType() {
|
||||
return DataType.BOOLEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnaryScalarFunction negate() {
|
||||
return new IsNull(location(), field());
|
||||
}
|
||||
}
|
|
@ -1,54 +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.nulls;
|
||||
|
||||
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;
|
||||
|
||||
public class IsNotNullProcessor implements Processor {
|
||||
|
||||
static final IsNotNullProcessor INSTANCE = new IsNotNullProcessor();
|
||||
|
||||
public static final String NAME = "ninn";
|
||||
|
||||
private IsNotNullProcessor() {}
|
||||
|
||||
public IsNotNullProcessor(StreamInput in) throws IOException {}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {}
|
||||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
return apply(input);
|
||||
}
|
||||
|
||||
public static Boolean apply(Object input) {
|
||||
return input != null ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return IsNotNullProcessor.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return obj == null || getClass() != obj.getClass();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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.nulls;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.CheckNullProcessor.CheckNullOperation;
|
||||
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;
|
||||
|
||||
public class IsNull extends UnaryScalarFunction implements Negatable<UnaryScalarFunction> {
|
||||
|
||||
public IsNull(Location location, Expression field) {
|
||||
super(location, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<IsNull> info() {
|
||||
return NodeInfo.create(this, IsNull::new, field());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IsNull replaceChild(Expression newChild) {
|
||||
return new IsNull(location(), newChild);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return field().fold() == null || DataTypes.isNull(field().dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Processor makeProcessor() {
|
||||
return new CheckNullProcessor(CheckNullOperation.IS_NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String processScript(String script) {
|
||||
return Scripts.formatTemplate(Scripts.SQL_SCRIPTS + ".isNull(" + script + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataType dataType() {
|
||||
return DataType.BOOLEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnaryScalarFunction negate() {
|
||||
return new IsNotNull(location(), field());
|
||||
}
|
||||
}
|
|
@ -6,12 +6,12 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class Equals extends BinaryComparison implements BinaryOperator.Negateable {
|
||||
public class Equals extends BinaryComparison implements Negatable<BinaryComparison> {
|
||||
|
||||
public Equals(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryComparisonOperation.EQ);
|
||||
|
@ -33,7 +33,7 @@ public class Equals extends BinaryComparison implements BinaryOperator.Negateabl
|
|||
}
|
||||
|
||||
@Override
|
||||
public BinaryOperator<?, ?, ?, ?> negate() {
|
||||
public BinaryComparison negate() {
|
||||
return new NotEquals(location(), left(), right());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class GreaterThan extends BinaryComparison implements Negateable {
|
||||
public class GreaterThan extends BinaryComparison implements Negatable<BinaryComparison> {
|
||||
|
||||
public GreaterThan(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryComparisonOperation.GT);
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class GreaterThanOrEqual extends BinaryComparison implements Negateable {
|
||||
public class GreaterThanOrEqual extends BinaryComparison implements Negatable<BinaryComparison> {
|
||||
|
||||
public GreaterThanOrEqual(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryComparisonOperation.GTE);
|
||||
|
|
|
@ -69,7 +69,7 @@ public class In extends NamedExpression implements ScriptWeaver {
|
|||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return Expressions.nullable(children());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class LessThan extends BinaryComparison implements Negateable {
|
||||
public class LessThan extends BinaryComparison implements Negatable<BinaryComparison> {
|
||||
|
||||
public LessThan(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryComparisonOperation.LT);
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class LessThanOrEqual extends BinaryComparison implements Negateable {
|
||||
public class LessThanOrEqual extends BinaryComparison implements Negatable<BinaryComparison> {
|
||||
|
||||
public LessThanOrEqual(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryComparisonOperation.LTE);
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Negatable;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Location;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
||||
public class NotEquals extends BinaryComparison implements BinaryOperator.Negateable {
|
||||
public class NotEquals extends BinaryComparison implements Negatable<BinaryComparison> {
|
||||
|
||||
public NotEquals(Location location, Expression left, Expression right) {
|
||||
super(location, left, right, BinaryComparisonOperation.NEQ);
|
||||
|
@ -33,7 +33,7 @@ public class NotEquals extends BinaryComparison implements BinaryOperator.Negate
|
|||
}
|
||||
|
||||
@Override
|
||||
public BinaryOperator<?, ?, ?, ?> negate() {
|
||||
public BinaryComparison negate() {
|
||||
return new Equals(location(), left(), right());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunctionAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator.Negateable;
|
||||
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.Coalesce;
|
||||
|
@ -46,6 +46,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
|
|||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Not;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Or;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNotNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparison;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.GreaterThan;
|
||||
|
@ -1159,8 +1160,11 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
if (((IsNotNull) e).field().nullable() == false) {
|
||||
return new Literal(e.location(), Expressions.name(e), Boolean.TRUE, DataType.BOOLEAN);
|
||||
}
|
||||
// see https://github.com/elastic/elasticsearch/issues/34876
|
||||
// similar for IsNull once it gets introduced
|
||||
|
||||
} else if (e instanceof IsNull) {
|
||||
if (((IsNull) e).field().nullable() == false) {
|
||||
return new Literal(e.location(), Expressions.name(e), Boolean.FALSE, DataType.BOOLEAN);
|
||||
}
|
||||
|
||||
} else if (e instanceof In) {
|
||||
In in = (In) e;
|
||||
|
@ -1171,6 +1175,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
} else if (e.nullable() && Expressions.anyMatch(e.children(), Expressions::isNull)) {
|
||||
return Literal.of(e, null);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
@ -1335,8 +1340,8 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
if (c instanceof Negateable) {
|
||||
return ((Negateable) c).negate();
|
||||
if (c instanceof Negatable) {
|
||||
return ((Negatable) c).negate();
|
||||
}
|
||||
|
||||
if (c instanceof Not) {
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.elasticsearch.xpack.sql.expression.UnresolvedStar;
|
|||
import org.elasticsearch.xpack.sql.expression.function.Function;
|
||||
import org.elasticsearch.xpack.sql.expression.function.UnresolvedFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.Cast;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Range;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
||||
|
@ -42,6 +42,7 @@ import org.elasticsearch.xpack.sql.expression.predicate.operator.arithmetic.Sub;
|
|||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.Equals;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.GreaterThan;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.GreaterThanOrEqual;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.In;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.LessThan;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.LessThanOrEqual;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.NotEquals;
|
||||
|
@ -211,8 +212,11 @@ abstract class ExpressionBuilder extends IdentifierBuilder {
|
|||
break;
|
||||
case SqlBaseParser.NULL:
|
||||
// shortcut to avoid double negation later on (since there's no IsNull (missing in ES is a negated exists))
|
||||
e = new IsNotNull(loc, exp);
|
||||
return pCtx.NOT() != null ? e : new Not(loc, e);
|
||||
if (pCtx.NOT() != null) {
|
||||
return new IsNotNull(loc, exp);
|
||||
} else {
|
||||
return new IsNull(loc, exp);
|
||||
}
|
||||
default:
|
||||
throw new ParsingException(loc, "Unknown predicate {}", pCtx.kind.getText());
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeHistogramFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.nulls.IsNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Range;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MatchQueryPredicate;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.fulltext.MultiMatchQueryPredicate;
|
||||
|
@ -109,7 +110,8 @@ final class QueryTranslator {
|
|||
new Ranges(),
|
||||
new BinaryLogic(),
|
||||
new Nots(),
|
||||
new Nulls(),
|
||||
new IsNullTranslator(),
|
||||
new IsNotNullTranslator(),
|
||||
new Likes(),
|
||||
new StringQueries(),
|
||||
new Matches(),
|
||||
|
@ -496,20 +498,41 @@ final class QueryTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
static class Nulls extends ExpressionTranslator<IsNotNull> {
|
||||
static class IsNotNullTranslator extends ExpressionTranslator<IsNotNull> {
|
||||
|
||||
@Override
|
||||
protected QueryTranslation asQuery(IsNotNull inn, boolean onAggs) {
|
||||
protected QueryTranslation asQuery(IsNotNull isNotNull, boolean onAggs) {
|
||||
Query query = null;
|
||||
AggFilter aggFilter = null;
|
||||
|
||||
if (onAggs) {
|
||||
aggFilter = new AggFilter(inn.id().toString(), inn.asScript());
|
||||
aggFilter = new AggFilter(isNotNull.id().toString(), isNotNull.asScript());
|
||||
} else {
|
||||
query = new ExistsQuery(inn.location(), nameOf(inn.field()));
|
||||
query = new ExistsQuery(isNotNull.location(), nameOf(isNotNull.field()));
|
||||
// query directly on the field
|
||||
if (inn.field() instanceof NamedExpression) {
|
||||
query = wrapIfNested(query, inn.field());
|
||||
if (isNotNull.field() instanceof NamedExpression) {
|
||||
query = wrapIfNested(query, isNotNull.field());
|
||||
}
|
||||
}
|
||||
|
||||
return new QueryTranslation(query, aggFilter);
|
||||
}
|
||||
}
|
||||
|
||||
static class IsNullTranslator extends ExpressionTranslator<IsNull> {
|
||||
|
||||
@Override
|
||||
protected QueryTranslation asQuery(IsNull isNull, boolean onAggs) {
|
||||
Query query = null;
|
||||
AggFilter aggFilter = null;
|
||||
|
||||
if (onAggs) {
|
||||
aggFilter = new AggFilter(isNull.id().toString(), isNull.asScript());
|
||||
} else {
|
||||
query = new NotQuery(isNull.location(), new ExistsQuery(isNull.location(), nameOf(isNull.field())));
|
||||
// query directly on the field
|
||||
if (isNull.field() instanceof NamedExpression) {
|
||||
query = wrapIfNested(query, isNull.field());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@ public class NotQuery extends Query {
|
|||
this.child = child;
|
||||
}
|
||||
|
||||
public Query child() {
|
||||
return child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsNestedField(String path, String field) {
|
||||
return child.containsNestedField(path, field);
|
||||
|
|
|
@ -33,7 +33,8 @@ class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalS
|
|||
Boolean and(Boolean, Boolean)
|
||||
Boolean or(Boolean, Boolean)
|
||||
Boolean not(Boolean)
|
||||
Boolean notNull(Object)
|
||||
Boolean isNull(Object)
|
||||
Boolean isNotNull(Object)
|
||||
|
||||
#
|
||||
# Null
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.nulls;
|
||||
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.Processors;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.ConstantProcessor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
|
||||
public class CheckNullProcessorTests extends AbstractWireSerializingTestCase<CheckNullProcessor> {
|
||||
|
||||
private static final Processor FALSE = new ConstantProcessor(false);
|
||||
private static final Processor TRUE = new ConstantProcessor(true);
|
||||
private static final Processor NULL = new ConstantProcessor((Object) null);
|
||||
|
||||
public static CheckNullProcessor randomProcessor() {
|
||||
return new CheckNullProcessor(randomFrom(CheckNullProcessor.CheckNullOperation.values()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CheckNullProcessor createTestInstance() {
|
||||
return randomProcessor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Reader<CheckNullProcessor> instanceReader() {
|
||||
return CheckNullProcessor::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedWriteableRegistry getNamedWriteableRegistry() {
|
||||
return new NamedWriteableRegistry(Processors.getNamedWriteables());
|
||||
}
|
||||
|
||||
public void testIsNull() {
|
||||
assertEquals(true, new CheckNullProcessor(CheckNullProcessor.CheckNullOperation.IS_NULL).process(null));
|
||||
assertEquals(false, new CheckNullProcessor(CheckNullProcessor.CheckNullOperation.IS_NULL).process("foo"));
|
||||
assertEquals(false, new CheckNullProcessor(CheckNullProcessor.CheckNullOperation.IS_NULL).process(1));
|
||||
}
|
||||
|
||||
public void testIsNotNull() {
|
||||
assertEquals(false, new CheckNullProcessor(CheckNullProcessor.CheckNullOperation.IS_NOT_NULL).process(null));
|
||||
assertEquals(true, new CheckNullProcessor(CheckNullProcessor.CheckNullOperation.IS_NOT_NULL).process("foo"));
|
||||
assertEquals(true, new CheckNullProcessor(CheckNullProcessor.CheckNullOperation.IS_NOT_NULL).process(1));
|
||||
}
|
||||
}
|
|
@ -35,6 +35,7 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.math.Floor;
|
|||
import org.elasticsearch.xpack.sql.expression.function.scalar.string.Ascii;
|
||||
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.nulls.IsNull;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.Range;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.Coalesce;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.And;
|
||||
|
@ -384,8 +385,16 @@ public class OptimizerTests extends ESTestCase {
|
|||
return ((Literal) new ConstantFolding().rule(b)).value();
|
||||
}
|
||||
|
||||
public void testNullFoldingIsNull() {
|
||||
FoldNull foldNull = new FoldNull();
|
||||
assertEquals(true, foldNull.rule(new IsNull(EMPTY, Literal.NULL)).fold());
|
||||
assertEquals(false, foldNull.rule(new IsNull(EMPTY, Literal.TRUE)).fold());
|
||||
}
|
||||
|
||||
public void testNullFoldingIsNotNull() {
|
||||
assertEquals(Literal.TRUE, new FoldNull().rule(new IsNotNull(EMPTY, Literal.TRUE)));
|
||||
FoldNull foldNull = new FoldNull();
|
||||
assertEquals(true, foldNull.rule(new IsNotNull(EMPTY, Literal.TRUE)).fold());
|
||||
assertEquals(false, foldNull.rule(new IsNotNull(EMPTY, Literal.NULL)).fold());
|
||||
}
|
||||
|
||||
public void testGenericNullableExpression() {
|
||||
|
@ -467,6 +476,12 @@ public class OptimizerTests extends ESTestCase {
|
|||
assertEquals(FIVE, eq.right());
|
||||
}
|
||||
|
||||
public void testBoolSimplifyNotIsNullAndNotIsNotNull() {
|
||||
BooleanSimplification simplification = new BooleanSimplification();
|
||||
assertTrue(simplification.rule(new Not(EMPTY, new IsNull(EMPTY, ONE))) instanceof IsNotNull);
|
||||
assertTrue(simplification.rule(new Not(EMPTY, new IsNotNull(EMPTY, ONE))) instanceof IsNull);
|
||||
}
|
||||
|
||||
public void testBoolSimplifyOr() {
|
||||
BooleanSimplification simplification = new BooleanSimplification();
|
||||
|
||||
|
|
|
@ -65,9 +65,14 @@ public class QueryFolderTests extends ESTestCase {
|
|||
assertThat(ee.output().get(0).toString(), startsWith("keyword{f}#"));
|
||||
}
|
||||
|
||||
public void testFoldingToLocalExecBooleanAndNull_WhereClause2() {
|
||||
PhysicalPlan p = plan("SELECT true OR null");
|
||||
public void testFoldingOfIsNull() {
|
||||
PhysicalPlan p = plan("SELECT keyword FROM test WHERE (keyword IS NOT NULL) IS NULL");
|
||||
assertEquals(LocalExec.class, p.getClass());
|
||||
LocalExec ee = (LocalExec) p;
|
||||
assertEquals(1, ee.output().size());
|
||||
assertThat(ee.output().get(0).toString(), startsWith("keyword{f}#"));
|
||||
}
|
||||
|
||||
public void testFoldingToLocalExecBooleanAndNull_WhereClause() {
|
||||
PhysicalPlan p = plan("SELECT keyword FROM test WHERE int > 10 AND null AND true");
|
||||
assertEquals(LocalExec.class, p.getClass());
|
||||
|
|
|
@ -19,6 +19,8 @@ import org.elasticsearch.xpack.sql.plan.logical.LogicalPlan;
|
|||
import org.elasticsearch.xpack.sql.plan.logical.Project;
|
||||
import org.elasticsearch.xpack.sql.planner.QueryTranslator.QueryTranslation;
|
||||
import org.elasticsearch.xpack.sql.querydsl.agg.AggFilter;
|
||||
import org.elasticsearch.xpack.sql.querydsl.query.ExistsQuery;
|
||||
import org.elasticsearch.xpack.sql.querydsl.query.NotQuery;
|
||||
import org.elasticsearch.xpack.sql.querydsl.query.Query;
|
||||
import org.elasticsearch.xpack.sql.querydsl.query.RangeQuery;
|
||||
import org.elasticsearch.xpack.sql.querydsl.query.ScriptQuery;
|
||||
|
@ -161,6 +163,62 @@ public class QueryTranslatorTests extends ESTestCase {
|
|||
assertEquals("Scalar function (LTRIM(keyword)) not allowed (yet) as arguments for LIKE", ex.getMessage());
|
||||
}
|
||||
|
||||
public void testTranslateIsNullExpression_WhereClause() {
|
||||
LogicalPlan p = plan("SELECT * FROM test WHERE keyword IS NULL");
|
||||
assertTrue(p instanceof Project);
|
||||
assertTrue(p.children().get(0) instanceof Filter);
|
||||
Expression condition = ((Filter) p.children().get(0)).condition();
|
||||
assertFalse(condition.foldable());
|
||||
QueryTranslation translation = QueryTranslator.toQuery(condition, false);
|
||||
assertTrue(translation.query instanceof NotQuery);
|
||||
NotQuery tq = (NotQuery) translation.query;
|
||||
assertTrue(tq.child() instanceof ExistsQuery);
|
||||
ExistsQuery eq = (ExistsQuery) tq.child();
|
||||
assertEquals("{\"exists\":{\"field\":\"keyword\",\"boost\":1.0}}",
|
||||
eq.asBuilder().toString().replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
public void testTranslateIsNotNullExpression_WhereClause() {
|
||||
LogicalPlan p = plan("SELECT * FROM test WHERE keyword IS NOT NULL");
|
||||
assertTrue(p instanceof Project);
|
||||
assertTrue(p.children().get(0) instanceof Filter);
|
||||
Expression condition = ((Filter) p.children().get(0)).condition();
|
||||
assertFalse(condition.foldable());
|
||||
QueryTranslation translation = QueryTranslator.toQuery(condition, false);
|
||||
assertTrue(translation.query instanceof ExistsQuery);
|
||||
ExistsQuery eq = (ExistsQuery) translation.query;
|
||||
assertEquals("{\"exists\":{\"field\":\"keyword\",\"boost\":1.0}}",
|
||||
eq.asBuilder().toString().replaceAll("\\s+", ""));
|
||||
}
|
||||
|
||||
public void testTranslateIsNullExpression_HavingClause_Painless() {
|
||||
LogicalPlan p = plan("SELECT keyword, max(int) FROM test GROUP BY keyword HAVING max(int) IS NULL");
|
||||
assertTrue(p instanceof Project);
|
||||
assertTrue(p.children().get(0) instanceof Filter);
|
||||
Expression condition = ((Filter) p.children().get(0)).condition();
|
||||
assertFalse(condition.foldable());
|
||||
QueryTranslation translation = QueryTranslator.toQuery(condition, true);
|
||||
assertNull(translation.query);
|
||||
AggFilter aggFilter = translation.aggFilter;
|
||||
assertEquals("InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.isNull(params.a0))",
|
||||
aggFilter.scriptTemplate().toString());
|
||||
assertThat(aggFilter.scriptTemplate().params().toString(), startsWith("[{a=MAX(int){a->"));
|
||||
}
|
||||
|
||||
public void testTranslateIsNotNullExpression_HavingClause_Painless() {
|
||||
LogicalPlan p = plan("SELECT keyword, max(int) FROM test GROUP BY keyword HAVING max(int) IS NOT NULL");
|
||||
assertTrue(p instanceof Project);
|
||||
assertTrue(p.children().get(0) instanceof Filter);
|
||||
Expression condition = ((Filter) p.children().get(0)).condition();
|
||||
assertFalse(condition.foldable());
|
||||
QueryTranslation translation = QueryTranslator.toQuery(condition, true);
|
||||
assertNull(translation.query);
|
||||
AggFilter aggFilter = translation.aggFilter;
|
||||
assertEquals("InternalSqlScriptUtils.nullSafeFilter(InternalSqlScriptUtils.isNotNull(params.a0))",
|
||||
aggFilter.scriptTemplate().toString());
|
||||
assertThat(aggFilter.scriptTemplate().params().toString(), startsWith("[{a=MAX(int){a->"));
|
||||
}
|
||||
|
||||
public void testTranslateInExpression_WhereClause() {
|
||||
LogicalPlan p = plan("SELECT * FROM test WHERE keyword IN ('foo', 'bar', 'lala', 'foo', concat('la', 'la'))");
|
||||
assertTrue(p instanceof Project);
|
||||
|
|
Loading…
Reference in New Issue