SQL: Fix issue with wrong NULL optimization (#37124)
Logical operators OR and AND as well as conditional functions (COALESCE, LEAST, GREATEST, etc.) cannot be folded to NULL if one of their children is NULL as is the case for most of the functions. Therefore, their nullable() implementation cannot return true. On the other hand they cannot return false as if they're wrapped within an IS NULL or IS NOT NULL expression, the expression will be folded to false and true respectively leading to wrong results. Change the signature of nullable() method and add a third value UKNOWN to handle these cases. Fixes: #35872
This commit is contained in:
parent
b34e7d4f19
commit
da3d8fb5b7
|
@ -76,7 +76,7 @@ public class Alias extends NamedExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
return child.nullable();
|
||||
}
|
||||
|
||||
|
@ -121,4 +121,4 @@ public class Alias extends NamedExpression {
|
|||
public String toString() {
|
||||
return child + " AS " + name() + "#" + id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ package org.elasticsearch.xpack.sql.expression;
|
|||
|
||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -42,20 +42,20 @@ public abstract class Attribute extends NamedExpression {
|
|||
private final String qualifier;
|
||||
|
||||
// can the attr be null - typically used in JOINs
|
||||
private final boolean nullable;
|
||||
private final Nullability nullability;
|
||||
|
||||
public Attribute(Source source, String name, String qualifier, ExpressionId id) {
|
||||
this(source, name, qualifier, true, id);
|
||||
this(source, name, qualifier, Nullability.TRUE, id);
|
||||
}
|
||||
|
||||
public Attribute(Source source, String name, String qualifier, boolean nullable, ExpressionId id) {
|
||||
this(source, name, qualifier, nullable, id, false);
|
||||
public Attribute(Source source, String name, String qualifier, Nullability nullability, ExpressionId id) {
|
||||
this(source, name, qualifier, nullability, id, false);
|
||||
}
|
||||
|
||||
public Attribute(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
public Attribute(Source source, String name, String qualifier, Nullability nullability, ExpressionId id, boolean synthetic) {
|
||||
super(source, name, emptyList(), id, synthetic);
|
||||
this.qualifier = qualifier;
|
||||
this.nullable = nullable;
|
||||
this.nullability = nullability;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,8 +77,8 @@ public abstract class Attribute extends NamedExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return nullable;
|
||||
public Nullability nullable() {
|
||||
return nullability;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -94,11 +94,11 @@ public abstract class Attribute extends NamedExpression {
|
|||
return Objects.equals(qualifier(), qualifier) ? this : clone(source(), name(), qualifier, nullable(), id(), synthetic());
|
||||
}
|
||||
|
||||
public Attribute withNullability(boolean nullable) {
|
||||
return Objects.equals(nullable(), nullable) ? this : clone(source(), name(), qualifier(), nullable, id(), synthetic());
|
||||
public Attribute withNullability(Nullability nullability) {
|
||||
return Objects.equals(nullable(), nullability) ? this : clone(source(), name(), qualifier(), nullability, id(), synthetic());
|
||||
}
|
||||
|
||||
protected abstract Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id,
|
||||
protected abstract Attribute clone(Source source, String name, String qualifier, Nullability nullability, ExpressionId id,
|
||||
boolean synthetic);
|
||||
|
||||
@Override
|
||||
|
@ -123,7 +123,7 @@ public abstract class Attribute extends NamedExpression {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), qualifier, nullable);
|
||||
return Objects.hash(super.hashCode(), qualifier, nullability);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -131,7 +131,7 @@ public abstract class Attribute extends NamedExpression {
|
|||
if (super.equals(obj)) {
|
||||
Attribute other = (Attribute) obj;
|
||||
return Objects.equals(qualifier, other.qualifier)
|
||||
&& Objects.equals(nullable, other.nullable);
|
||||
&& Objects.equals(nullability, other.nullability);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -143,4 +143,4 @@ public abstract class Attribute extends NamedExpression {
|
|||
}
|
||||
|
||||
protected abstract String label();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class Exists extends SubQueryExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,8 +78,7 @@ public abstract class Expression extends Node<Expression> implements Resolvable
|
|||
throw new SqlIllegalArgumentException("Should not fold expression");
|
||||
}
|
||||
|
||||
// whether the expression becomes null if at least one param/input is null
|
||||
public abstract boolean nullable();
|
||||
public abstract Nullability nullable();
|
||||
|
||||
// the references/inputs/leaves of the expression tree
|
||||
public AttributeSet references() {
|
||||
|
|
|
@ -79,13 +79,8 @@ public final class Expressions {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean nullable(List<? extends Expression> exps) {
|
||||
for (Expression exp : exps) {
|
||||
if (exp.nullable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
public static Nullability nullable(List<? extends Expression> exps) {
|
||||
return Nullability.and(exps.stream().map(Expression::nullable).toArray(Nullability[]::new));
|
||||
}
|
||||
|
||||
public static boolean foldable(List<? extends Expression> exps) {
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
package org.elasticsearch.xpack.sql.expression;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.EsField;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
|
@ -34,12 +34,12 @@ public class FieldAttribute extends TypedAttribute {
|
|||
}
|
||||
|
||||
public FieldAttribute(Source source, FieldAttribute parent, String name, EsField field) {
|
||||
this(source, parent, name, field, null, true, null, false);
|
||||
this(source, parent, name, field, null, Nullability.TRUE, null, false);
|
||||
}
|
||||
|
||||
public FieldAttribute(Source source, FieldAttribute parent, String name, EsField field, String qualifier,
|
||||
boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
super(source, name, field.getDataType(), qualifier, nullable, id, synthetic);
|
||||
Nullability nullability, ExpressionId id, boolean synthetic) {
|
||||
super(source, name, field.getDataType(), qualifier, nullability, id, synthetic);
|
||||
this.path = parent != null ? parent.name() : StringUtils.EMPTY;
|
||||
this.parent = parent;
|
||||
this.field = field;
|
||||
|
@ -98,13 +98,14 @@ public class FieldAttribute extends TypedAttribute {
|
|||
|
||||
@Override
|
||||
protected Expression canonicalize() {
|
||||
return new FieldAttribute(source(), null, "<none>", field, null, true, id(), false);
|
||||
return new FieldAttribute(source(), null, "<none>", field, null, Nullability.TRUE, id(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
protected Attribute clone(Source source, String name, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
FieldAttribute qualifiedParent = parent != null ? (FieldAttribute) parent.withQualifier(qualifier) : null;
|
||||
return new FieldAttribute(source, qualifiedParent, name, field, qualifier, nullable, id, synthetic);
|
||||
return new FieldAttribute(source, qualifiedParent, name, field, qualifier, nullability, id, synthetic);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,8 +8,8 @@ package org.elasticsearch.xpack.sql.expression;
|
|||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||
|
@ -56,8 +56,8 @@ public class Literal extends NamedExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return value == null;
|
||||
public Nullability nullable() {
|
||||
return value == null ? Nullability.TRUE : Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,7 +77,7 @@ public class Literal extends NamedExpression {
|
|||
|
||||
@Override
|
||||
public Attribute toAttribute() {
|
||||
return new LiteralAttribute(source(), name(), null, false, id(), false, dataType, this);
|
||||
return new LiteralAttribute(source(), name(), null, Nullability.FALSE, id(), false, dataType, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,4 +168,4 @@ public class Literal extends NamedExpression {
|
|||
String name = source instanceof NamedExpression ? ((NamedExpression) source).name() : String.valueOf(value);
|
||||
return new Literal(source.source(), name, value, source.dataType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,17 +6,17 @@
|
|||
package org.elasticsearch.xpack.sql.expression;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
public class LiteralAttribute extends TypedAttribute {
|
||||
|
||||
private final Literal literal;
|
||||
|
||||
public LiteralAttribute(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic,
|
||||
DataType dataType, Literal literal) {
|
||||
super(source, name, dataType, qualifier, nullable, id, synthetic);
|
||||
public LiteralAttribute(Source source, String name, String qualifier, Nullability nullability, ExpressionId id, boolean synthetic,
|
||||
DataType dataType, Literal literal) {
|
||||
super(source, name, dataType, qualifier, nullability, id, synthetic);
|
||||
this.literal = literal;
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@ public class LiteralAttribute extends TypedAttribute {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected LiteralAttribute clone(Source source, String name, String qualifier, boolean nullable,
|
||||
protected LiteralAttribute clone(Source source, String name, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
return new LiteralAttribute(source, name, qualifier, nullable, id, synthetic, dataType(), literal);
|
||||
return new LiteralAttribute(source, name, qualifier, nullability, id, synthetic, dataType(), literal);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -41,4 +41,4 @@ public class LiteralAttribute extends TypedAttribute {
|
|||
public Pipe asPipe() {
|
||||
return literal.asPipe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
public enum Nullability {
|
||||
TRUE, // Whether the expression can become null
|
||||
FALSE, // The expression can never become null
|
||||
UNKNOWN; // Cannot determine if the expression supports possible null folding
|
||||
|
||||
/**
|
||||
* Return the logical AND of a list of {@code Nullability}
|
||||
* <pre>
|
||||
* UNKNOWN AND TRUE/FALSE/UNKNOWN = UNKNOWN
|
||||
* FALSE AND FALSE = FALSE
|
||||
* TRUE AND FALSE/TRUE = TRUE
|
||||
* </pre>
|
||||
*/
|
||||
public static Nullability and(Nullability... nullabilities) {
|
||||
Nullability value = null;
|
||||
for (Nullability n: nullabilities) {
|
||||
switch (n) {
|
||||
case UNKNOWN:
|
||||
return UNKNOWN;
|
||||
case TRUE:
|
||||
value = TRUE;
|
||||
break;
|
||||
case FALSE:
|
||||
if (value == null) {
|
||||
value = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value != null ? value : FALSE;
|
||||
}
|
||||
}
|
|
@ -41,8 +41,8 @@ public class Order extends Expression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,4 +95,4 @@ public class Order extends Expression {
|
|||
&& Objects.equals(nulls, other.nulls)
|
||||
&& Objects.equals(child, other.child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public class ScalarSubquery extends SubQueryExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return true;
|
||||
public Nullability nullable() {
|
||||
return Nullability.TRUE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,9 @@ public abstract class TypedAttribute extends Attribute {
|
|||
|
||||
private final DataType dataType;
|
||||
|
||||
protected TypedAttribute(Source source, String name, DataType dataType) {
|
||||
this(source, name, dataType, null, true, null, false);
|
||||
}
|
||||
|
||||
protected TypedAttribute(Source source, String name, DataType dataType, String qualifier, boolean nullable,
|
||||
protected TypedAttribute(Source source, String name, DataType dataType, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
super(source, name, qualifier, nullable, id, synthetic);
|
||||
super(source, name, qualifier, nullability, id, synthetic);
|
||||
this.dataType = dataType;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public abstract class UnaryExpression extends Expression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
return child.nullable();
|
||||
}
|
||||
|
||||
|
@ -73,4 +73,4 @@ public abstract class UnaryExpression extends Expression {
|
|||
UnaryExpression other = (UnaryExpression) obj;
|
||||
return Objects.equals(child, other.child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class UnresolvedAlias extends UnresolvedNamedExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
throw new UnresolvedException("nullable", this);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ package org.elasticsearch.xpack.sql.expression;
|
|||
|
||||
import org.elasticsearch.xpack.sql.capabilities.Unresolvable;
|
||||
import org.elasticsearch.xpack.sql.capabilities.UnresolvedException;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
import org.elasticsearch.xpack.sql.util.CollectionUtils;
|
||||
|
||||
|
@ -65,7 +65,8 @@ public class UnresolvedAttribute extends Attribute implements Unresolvable {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
protected Attribute clone(Source source, String name, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public class UnresolvedStar extends UnresolvedNamedExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
throw new UnresolvedException("nullable", this);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.expression.Expression;
|
|||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||
|
||||
|
@ -45,7 +46,7 @@ public abstract class Function extends NamedExpression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
return Expressions.nullable(children());
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.TypedAttribute;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
@ -16,9 +17,9 @@ public abstract class FunctionAttribute extends TypedAttribute {
|
|||
|
||||
private final String functionId;
|
||||
|
||||
protected FunctionAttribute(Source source, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id,
|
||||
boolean synthetic, String functionId) {
|
||||
super(source, name, dataType, qualifier, nullable, id, synthetic);
|
||||
protected FunctionAttribute(Source source, String name, DataType dataType, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic, String functionId) {
|
||||
super(source, name, dataType, qualifier, nullability, id, synthetic);
|
||||
this.functionId = functionId;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,12 +7,15 @@ package org.elasticsearch.xpack.sql.expression.function;
|
|||
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.ScorePipe;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.Nullability.FALSE;
|
||||
|
||||
/**
|
||||
* {@link Attribute} that represents Elasticsearch's {@code _score}.
|
||||
*/
|
||||
|
@ -21,15 +24,15 @@ public class ScoreAttribute extends FunctionAttribute {
|
|||
* Constructor for normal use.
|
||||
*/
|
||||
public ScoreAttribute(Source source) {
|
||||
this(source, "SCORE()", DataType.FLOAT, null, false, null, false);
|
||||
this(source, "SCORE()", DataType.FLOAT, null, FALSE, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for {@link #clone()}
|
||||
*/
|
||||
private ScoreAttribute(Source source, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id,
|
||||
private ScoreAttribute(Source source, String name, DataType dataType, String qualifier, Nullability nullability, ExpressionId id,
|
||||
boolean synthetic) {
|
||||
super(source, name, dataType, qualifier, nullable, id, synthetic, "SCORE");
|
||||
super(source, name, dataType, qualifier, nullability, id, synthetic, "SCORE");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,8 +41,9 @@ public class ScoreAttribute extends FunctionAttribute {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
return new ScoreAttribute(source, name, dataType(), qualifier, nullable, id, synthetic);
|
||||
protected Attribute clone(Source source, String name, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
return new ScoreAttribute(source, name, dataType(), qualifier, nullability, id, synthetic);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.xpack.sql.capabilities.UnresolvedException;
|
|||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.session.Configuration;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
@ -135,7 +136,7 @@ public class UnresolvedFunction extends Function implements Unresolvable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
throw new UnresolvedException("nullable", this);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ package org.elasticsearch.xpack.sql.expression.function.aggregate;
|
|||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -21,12 +22,12 @@ public class AggregateFunctionAttribute extends FunctionAttribute {
|
|||
|
||||
AggregateFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id,
|
||||
String functionId, String propertyPath) {
|
||||
this(source, name, dataType, null, false, id, false, functionId, propertyPath);
|
||||
this(source, name, dataType, null, Nullability.FALSE, id, false, functionId, propertyPath);
|
||||
}
|
||||
|
||||
public AggregateFunctionAttribute(Source source, String name, DataType dataType, String qualifier,
|
||||
boolean nullable, ExpressionId id, boolean synthetic, String functionId, String propertyPath) {
|
||||
super(source, name, dataType, qualifier, nullable, id, synthetic, functionId);
|
||||
Nullability nullability, ExpressionId id, boolean synthetic, String functionId, String propertyPath) {
|
||||
super(source, name, dataType, qualifier, nullability, id, synthetic, functionId);
|
||||
this.propertyPath = propertyPath;
|
||||
}
|
||||
|
||||
|
@ -42,14 +43,14 @@ public class AggregateFunctionAttribute extends FunctionAttribute {
|
|||
|
||||
@Override
|
||||
protected Expression canonicalize() {
|
||||
return new AggregateFunctionAttribute(source(), "<none>", dataType(), null, true, id(), false, "<none>", null);
|
||||
return new AggregateFunctionAttribute(source(), "<none>", dataType(), null, Nullability.TRUE, id(), false, "<none>", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
protected Attribute clone(Source source, String name, String qualifier, Nullability nullability, ExpressionId id, boolean synthetic) {
|
||||
// this is highly correlated with QueryFolder$FoldAggregate#addFunction (regarding the function name within the querydsl)
|
||||
// that is the functionId is actually derived from the expression id to easily track it across contexts
|
||||
return new AggregateFunctionAttribute(source, name, dataType(), qualifier, nullable, id, synthetic, functionId(), propertyPath);
|
||||
return new AggregateFunctionAttribute(source, name, dataType(), qualifier, nullability, id, synthetic, functionId(), propertyPath);
|
||||
}
|
||||
|
||||
public AggregateFunctionAttribute withFunctionId(String functionId, String propertyPath) {
|
||||
|
|
|
@ -8,20 +8,21 @@ package org.elasticsearch.xpack.sql.expression.function.grouping;
|
|||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
public class GroupingFunctionAttribute extends FunctionAttribute {
|
||||
|
||||
GroupingFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id, String functionId) {
|
||||
this(source, name, dataType, null, false, id, false, functionId);
|
||||
this(source, name, dataType, null, Nullability.FALSE, id, false, functionId);
|
||||
}
|
||||
|
||||
public GroupingFunctionAttribute(Source source, String name, DataType dataType, String qualifier,
|
||||
boolean nullable, ExpressionId id, boolean synthetic, String functionId) {
|
||||
super(source, name, dataType, qualifier, nullable, id, synthetic, functionId);
|
||||
Nullability nullability, ExpressionId id, boolean synthetic, String functionId) {
|
||||
super(source, name, dataType, qualifier, nullability, id, synthetic, functionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -32,14 +33,15 @@ public class GroupingFunctionAttribute extends FunctionAttribute {
|
|||
|
||||
@Override
|
||||
protected Expression canonicalize() {
|
||||
return new GroupingFunctionAttribute(source(), "<none>", dataType(), null, true, id(), false, "<none>");
|
||||
return new GroupingFunctionAttribute(source(), "<none>", dataType(), null, Nullability.TRUE, id(), false, "<none>");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
protected Attribute clone(Source source, String name, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
// this is highly correlated with QueryFolder$FoldAggregate#addFunction (regarding the function name within the querydsl)
|
||||
// that is the functionId is actually derived from the expression id to easily track it across contexts
|
||||
return new GroupingFunctionAttribute(source, name, dataType(), qualifier, nullable, id, synthetic, functionId());
|
||||
return new GroupingFunctionAttribute(source, name, dataType(), qualifier, nullability, id, synthetic, functionId());
|
||||
}
|
||||
|
||||
public GroupingFunctionAttribute withFunctionId(String functionId, String propertyPath) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
@ -62,8 +63,11 @@ public class Cast extends UnaryScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return field().nullable() || DataTypes.isNull(from());
|
||||
public Nullability nullable() {
|
||||
if (DataTypes.isNull(from())) {
|
||||
return Nullability.TRUE;
|
||||
}
|
||||
return field().nullable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.function.scalar;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.session.Configuration;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
|
@ -42,8 +43,8 @@ public abstract class ConfigurationFunction extends ScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,4 +74,4 @@ public abstract class ConfigurationFunction extends ScalarFunction {
|
|||
public boolean equals(Object obj) {
|
||||
return super.equals(obj) && Objects.equals(fold(), ((ConfigurationFunction) obj).fold());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,12 @@ package org.elasticsearch.xpack.sql.expression.function.scalar;
|
|||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -25,13 +26,13 @@ public class ScalarFunctionAttribute extends FunctionAttribute {
|
|||
|
||||
ScalarFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id,
|
||||
String functionId, ScriptTemplate script, Expression orderBy, Pipe processorDef) {
|
||||
this(source, name, dataType, null, true, id, false, functionId, script, orderBy, processorDef);
|
||||
this(source, name, dataType, null, Nullability.TRUE, id, false, functionId, script, orderBy, processorDef);
|
||||
}
|
||||
|
||||
public ScalarFunctionAttribute(Source source, String name, DataType dataType, String qualifier,
|
||||
boolean nullable, ExpressionId id, boolean synthetic, String functionId, ScriptTemplate script,
|
||||
Expression orderBy, Pipe pipe) {
|
||||
super(source, name, dataType, qualifier, nullable, id, synthetic, functionId);
|
||||
Nullability nullability, ExpressionId id, boolean synthetic, String functionId, ScriptTemplate script,
|
||||
Expression orderBy, Pipe pipe) {
|
||||
super(source, name, dataType, qualifier, nullability, id, synthetic, functionId);
|
||||
|
||||
this.script = script;
|
||||
this.orderBy = orderBy;
|
||||
|
@ -60,14 +61,15 @@ public class ScalarFunctionAttribute extends FunctionAttribute {
|
|||
|
||||
@Override
|
||||
protected Expression canonicalize() {
|
||||
return new ScalarFunctionAttribute(source(), "<none>", dataType(), null, true, id(), false,
|
||||
return new ScalarFunctionAttribute(source(), "<none>", dataType(), null, Nullability.TRUE, id(), false,
|
||||
functionId(), script, orderBy, pipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute clone(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic) {
|
||||
return new ScalarFunctionAttribute(source, name, dataType(), qualifier, nullable, id, synthetic,
|
||||
functionId(), script, orderBy, pipe);
|
||||
protected Attribute clone(Source source, String name, String qualifier, Nullability nullability,
|
||||
ExpressionId id, boolean synthetic) {
|
||||
return new ScalarFunctionAttribute(source, name, dataType(), qualifier, nullability,
|
||||
id, synthetic, functionId(), script, orderBy, pipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,4 +92,4 @@ public class ScalarFunctionAttribute extends FunctionAttribute {
|
|||
protected String label() {
|
||||
return "s->" + functionId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.xpack.sql.expression.Expression;
|
|||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.BinaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
|
@ -50,8 +51,8 @@ public class Concat extends BinaryScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,4 +86,4 @@ public class Concat extends BinaryScalarFunction {
|
|||
public DataType dataType() {
|
||||
return DataType.KEYWORD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate;
|
|||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
|
||||
|
@ -119,8 +120,8 @@ public class Range extends ScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return value.nullable() && lower.nullable() && upper.nullable();
|
||||
public Nullability nullable() {
|
||||
return Nullability.and(value.nullable(), lower.nullable(), upper.nullable());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -216,4 +217,4 @@ public class Range extends ScalarFunction {
|
|||
public String toString() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ 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.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
@ -36,7 +37,7 @@ public abstract class ConditionalFunction extends ScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ 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.Nullability;
|
||||
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;
|
||||
|
@ -57,8 +58,8 @@ public class NullIf extends ConditionalFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return true;
|
||||
public Nullability nullable() {
|
||||
return Nullability.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.fulltext;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
|
@ -56,8 +57,8 @@ public abstract class FullTextPredicate extends Expression {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -84,4 +85,4 @@ public abstract class FullTextPredicate extends Expression {
|
|||
return Objects.equals(query, other.query)
|
||||
&& Objects.equals(options, other.options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.xpack.sql.expression.predicate.logical;
|
|||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||
|
@ -35,8 +36,8 @@ public abstract class BinaryLogic extends BinaryOperator<Boolean, Boolean, Boole
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
public Nullability nullable() {
|
||||
// Cannot fold null due to 3vl, constant folding will do any possible folding.
|
||||
return false;
|
||||
return Nullability.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.nulls;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
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;
|
||||
|
@ -48,8 +49,8 @@ public class IsNotNull extends UnaryScalarFunction implements Negatable<UnarySca
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.nulls;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
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;
|
||||
|
@ -48,8 +49,8 @@ public class IsNull extends UnaryScalarFunction implements Negatable<UnaryScalar
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
|||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||
|
@ -64,8 +65,8 @@ public class In extends ScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
|
@ -35,7 +36,7 @@ public class NullEquals extends BinaryComparison {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.elasticsearch.xpack.sql.expression.predicate.regex;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.function.scalar.UnaryScalarFunction;
|
||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.RegexOperation;
|
||||
|
@ -28,8 +29,11 @@ public abstract class RegexMatch extends UnaryScalarFunction {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return field().nullable() && pattern != null;
|
||||
public Nullability nullable() {
|
||||
if (pattern == null) {
|
||||
return Nullability.TRUE;
|
||||
}
|
||||
return field().nullable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.xpack.sql.expression.Expressions;
|
|||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.Order;
|
||||
import org.elasticsearch.xpack.sql.expression.function.Function;
|
||||
import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute;
|
||||
|
@ -44,7 +45,6 @@ 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;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Not;
|
||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Or;
|
||||
|
@ -1097,12 +1097,12 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
@Override
|
||||
protected Expression rule(Expression e) {
|
||||
if (e instanceof IsNotNull) {
|
||||
if (((IsNotNull) e).field().nullable() == false) {
|
||||
if (((IsNotNull) e).field().nullable() == Nullability.FALSE) {
|
||||
return new Literal(e.source(), Expressions.name(e), Boolean.TRUE, DataType.BOOLEAN);
|
||||
}
|
||||
|
||||
} else if (e instanceof IsNull) {
|
||||
if (((IsNull) e).field().nullable() == false) {
|
||||
if (((IsNull) e).field().nullable() == Nullability.FALSE) {
|
||||
return new Literal(e.source(), Expressions.name(e), Boolean.FALSE, DataType.BOOLEAN);
|
||||
}
|
||||
|
||||
|
@ -1112,10 +1112,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
return Literal.of(in, null);
|
||||
}
|
||||
|
||||
} else if (e instanceof NullIf) {
|
||||
return e;
|
||||
|
||||
} else if (e.nullable() && Expressions.anyMatch(e.children(), Expressions::isNull)) {
|
||||
} else if (e.nullable() == Nullability.TRUE && Expressions.anyMatch(e.children(), Expressions::isNull)) {
|
||||
return Literal.of(e, null);
|
||||
}
|
||||
|
||||
|
@ -1314,7 +1311,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
|
||||
// true for equality
|
||||
if (bc instanceof Equals || bc instanceof GreaterThanOrEqual || bc instanceof LessThanOrEqual) {
|
||||
if (!l.nullable() && !r.nullable() && l.semanticEquals(r)) {
|
||||
if (l.nullable() == Nullability.FALSE && r.nullable() == Nullability.FALSE && l.semanticEquals(r)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -1329,7 +1326,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
|||
|
||||
// false for equality
|
||||
if (bc instanceof NotEquals || bc instanceof GreaterThan || bc instanceof LessThan) {
|
||||
if (!l.nullable() && !r.nullable() && l.semanticEquals(r)) {
|
||||
if (l.nullable() == Nullability.FALSE && r.nullable() == Nullability.FALSE && l.semanticEquals(r)) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,17 @@
|
|||
*/
|
||||
package org.elasticsearch.xpack.sql.plan.logical;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.elasticsearch.xpack.sql.expression.Attribute;
|
||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||
import org.elasticsearch.xpack.sql.tree.Source;
|
||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.sql.type.DataType;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.util.CollectionUtils.combine;
|
||||
|
||||
public class Join extends BinaryPlan {
|
||||
|
@ -78,7 +78,7 @@ public class Join extends BinaryPlan {
|
|||
|
||||
private static List<Attribute> makeNullable(List<Attribute> output) {
|
||||
return output.stream()
|
||||
.map(a -> a.withNullability(true))
|
||||
.map(a -> a.withNullability(Nullability.TRUE))
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
package org.elasticsearch.xpack.sql.expression;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import static org.elasticsearch.xpack.sql.expression.Nullability.FALSE;
|
||||
import static org.elasticsearch.xpack.sql.expression.Nullability.TRUE;
|
||||
import static org.elasticsearch.xpack.sql.expression.Nullability.UNKNOWN;
|
||||
|
||||
public class NullabilityTests extends ESTestCase {
|
||||
|
||||
public void testLogicalAndOfNullabilities() {
|
||||
assertEquals(FALSE, Nullability.and());
|
||||
|
||||
assertEquals(TRUE, Nullability.and(TRUE));
|
||||
assertEquals(FALSE, Nullability.and(FALSE));
|
||||
assertEquals(UNKNOWN, Nullability.and(UNKNOWN));
|
||||
|
||||
assertEquals(UNKNOWN, Nullability.and(UNKNOWN, UNKNOWN));
|
||||
assertEquals(UNKNOWN, Nullability.and(UNKNOWN, TRUE));
|
||||
assertEquals(UNKNOWN, Nullability.and(UNKNOWN, FALSE));
|
||||
|
||||
assertEquals(FALSE, Nullability.and(FALSE, FALSE));
|
||||
assertEquals(TRUE, Nullability.and(FALSE, TRUE));
|
||||
assertEquals(UNKNOWN, Nullability.and(FALSE, UNKNOWN));
|
||||
|
||||
assertEquals(TRUE, Nullability.and(TRUE, TRUE));
|
||||
assertEquals(TRUE, Nullability.and(TRUE, FALSE));
|
||||
assertEquals(UNKNOWN, Nullability.and(TRUE, UNKNOWN));
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
|||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
||||
import org.elasticsearch.xpack.sql.expression.Nullability;
|
||||
import org.elasticsearch.xpack.sql.expression.Order;
|
||||
import org.elasticsearch.xpack.sql.expression.Order.OrderDirection;
|
||||
import org.elasticsearch.xpack.sql.expression.function.Function;
|
||||
|
@ -134,8 +135,8 @@ public class OptimizerTests extends ESTestCase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean nullable() {
|
||||
return false;
|
||||
public Nullability nullable() {
|
||||
return Nullability.FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -393,6 +394,8 @@ public class OptimizerTests extends ESTestCase {
|
|||
return ((Literal) new ConstantFolding().rule(b)).value();
|
||||
}
|
||||
|
||||
// Null folding
|
||||
|
||||
public void testNullFoldingIsNull() {
|
||||
FoldNull foldNull = new FoldNull();
|
||||
assertEquals(true, foldNull.rule(new IsNull(EMPTY, Literal.NULL)).fold());
|
||||
|
@ -422,6 +425,34 @@ public class OptimizerTests extends ESTestCase {
|
|||
assertNullLiteral(rule.rule(new RLike(EMPTY, Literal.NULL, "123")));
|
||||
}
|
||||
|
||||
public void testNullFoldingDoesNotApplyOnLogicalExpressions() {
|
||||
FoldNull rule = new FoldNull();
|
||||
|
||||
Or or = new Or(EMPTY, Literal.NULL, Literal.TRUE);
|
||||
assertEquals(or, rule.rule(or));
|
||||
or = new Or(EMPTY, Literal.NULL, Literal.NULL);
|
||||
assertEquals(or, rule.rule(or));
|
||||
|
||||
And and = new And(EMPTY, Literal.NULL, Literal.TRUE);
|
||||
assertEquals(and, rule.rule(and));
|
||||
and = new And(EMPTY, Literal.NULL, Literal.NULL);
|
||||
assertEquals(and, rule.rule(and));
|
||||
}
|
||||
|
||||
public void testNullFoldingDoesNotApplyOnConditionals() {
|
||||
FoldNull rule = new FoldNull();
|
||||
|
||||
Coalesce coalesce = new Coalesce(EMPTY, Arrays.asList(Literal.NULL, ONE, TWO));
|
||||
assertEquals(coalesce, rule.rule(coalesce));
|
||||
coalesce = new Coalesce(EMPTY, Arrays.asList(Literal.NULL, NULL, NULL));
|
||||
assertEquals(coalesce, rule.rule(coalesce));
|
||||
|
||||
Greatest greatest = new Greatest(EMPTY, Arrays.asList(Literal.NULL, ONE, TWO));
|
||||
assertEquals(greatest, rule.rule(greatest));
|
||||
greatest = new Greatest(EMPTY, Arrays.asList(Literal.NULL, ONE, TWO));
|
||||
assertEquals(greatest, rule.rule(greatest));
|
||||
}
|
||||
|
||||
public void testSimplifyCoalesceNulls() {
|
||||
Expression e = new SimplifyConditional().rule(new Coalesce(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
||||
assertEquals(Coalesce.class, e.getClass());
|
||||
|
|
Loading…
Reference in New Issue