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
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return child.nullable();
|
return child.nullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ package org.elasticsearch.xpack.sql.expression;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -42,20 +42,20 @@ public abstract class Attribute extends NamedExpression {
|
||||||
private final String qualifier;
|
private final String qualifier;
|
||||||
|
|
||||||
// can the attr be null - typically used in JOINs
|
// 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) {
|
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) {
|
public Attribute(Source source, String name, String qualifier, Nullability nullability, ExpressionId id) {
|
||||||
this(source, name, qualifier, nullable, id, false);
|
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);
|
super(source, name, emptyList(), id, synthetic);
|
||||||
this.qualifier = qualifier;
|
this.qualifier = qualifier;
|
||||||
this.nullable = nullable;
|
this.nullability = nullability;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,8 +77,8 @@ public abstract class Attribute extends NamedExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return nullable;
|
return nullability;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,11 +94,11 @@ public abstract class Attribute extends NamedExpression {
|
||||||
return Objects.equals(qualifier(), qualifier) ? this : clone(source(), name(), qualifier, nullable(), id(), synthetic());
|
return Objects.equals(qualifier(), qualifier) ? this : clone(source(), name(), qualifier, nullable(), id(), synthetic());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Attribute withNullability(boolean nullable) {
|
public Attribute withNullability(Nullability nullability) {
|
||||||
return Objects.equals(nullable(), nullable) ? this : clone(source(), name(), qualifier(), nullable, id(), synthetic());
|
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);
|
boolean synthetic);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -123,7 +123,7 @@ public abstract class Attribute extends NamedExpression {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(super.hashCode(), qualifier, nullable);
|
return Objects.hash(super.hashCode(), qualifier, nullability);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -131,7 +131,7 @@ public abstract class Attribute extends NamedExpression {
|
||||||
if (super.equals(obj)) {
|
if (super.equals(obj)) {
|
||||||
Attribute other = (Attribute) obj;
|
Attribute other = (Attribute) obj;
|
||||||
return Objects.equals(qualifier, other.qualifier)
|
return Objects.equals(qualifier, other.qualifier)
|
||||||
&& Objects.equals(nullable, other.nullable);
|
&& Objects.equals(nullability, other.nullability);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class Exists extends SubQueryExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,7 @@ public abstract class Expression extends Node<Expression> implements Resolvable
|
||||||
throw new SqlIllegalArgumentException("Should not fold expression");
|
throw new SqlIllegalArgumentException("Should not fold expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
// whether the expression becomes null if at least one param/input is null
|
public abstract Nullability nullable();
|
||||||
public abstract boolean nullable();
|
|
||||||
|
|
||||||
// the references/inputs/leaves of the expression tree
|
// the references/inputs/leaves of the expression tree
|
||||||
public AttributeSet references() {
|
public AttributeSet references() {
|
||||||
|
|
|
@ -79,13 +79,8 @@ public final class Expressions {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean nullable(List<? extends Expression> exps) {
|
public static Nullability nullable(List<? extends Expression> exps) {
|
||||||
for (Expression exp : exps) {
|
return Nullability.and(exps.stream().map(Expression::nullable).toArray(Nullability[]::new));
|
||||||
if (exp.nullable()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean foldable(List<? extends Expression> exps) {
|
public static boolean foldable(List<? extends Expression> exps) {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
package org.elasticsearch.xpack.sql.expression;
|
package org.elasticsearch.xpack.sql.expression;
|
||||||
|
|
||||||
import org.elasticsearch.common.Strings;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
import org.elasticsearch.xpack.sql.type.EsField;
|
import org.elasticsearch.xpack.sql.type.EsField;
|
||||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
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) {
|
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,
|
public FieldAttribute(Source source, FieldAttribute parent, String name, EsField field, String qualifier,
|
||||||
boolean nullable, ExpressionId id, boolean synthetic) {
|
Nullability nullability, ExpressionId id, boolean synthetic) {
|
||||||
super(source, name, field.getDataType(), qualifier, nullable, id, synthetic);
|
super(source, name, field.getDataType(), qualifier, nullability, id, synthetic);
|
||||||
this.path = parent != null ? parent.name() : StringUtils.EMPTY;
|
this.path = parent != null ? parent.name() : StringUtils.EMPTY;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.field = field;
|
this.field = field;
|
||||||
|
@ -98,13 +98,14 @@ public class FieldAttribute extends TypedAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Expression canonicalize() {
|
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
|
@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;
|
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
|
@Override
|
||||||
|
|
|
@ -8,8 +8,8 @@ package org.elasticsearch.xpack.sql.expression;
|
||||||
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
import org.elasticsearch.xpack.sql.SqlIllegalArgumentException;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
|
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
import org.elasticsearch.xpack.sql.type.DataTypeConversion;
|
||||||
import org.elasticsearch.xpack.sql.type.DataTypes;
|
import org.elasticsearch.xpack.sql.type.DataTypes;
|
||||||
|
@ -56,8 +56,8 @@ public class Literal extends NamedExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return value == null;
|
return value == null ? Nullability.TRUE : Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -77,7 +77,7 @@ public class Literal extends NamedExpression {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attribute toAttribute() {
|
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
|
@Override
|
||||||
|
|
|
@ -6,17 +6,17 @@
|
||||||
package org.elasticsearch.xpack.sql.expression;
|
package org.elasticsearch.xpack.sql.expression;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
|
||||||
public class LiteralAttribute extends TypedAttribute {
|
public class LiteralAttribute extends TypedAttribute {
|
||||||
|
|
||||||
private final Literal literal;
|
private final Literal literal;
|
||||||
|
|
||||||
public LiteralAttribute(Source source, String name, String qualifier, boolean nullable, ExpressionId id, boolean synthetic,
|
public LiteralAttribute(Source source, String name, String qualifier, Nullability nullability, ExpressionId id, boolean synthetic,
|
||||||
DataType dataType, Literal literal) {
|
DataType dataType, Literal literal) {
|
||||||
super(source, name, dataType, qualifier, nullable, id, synthetic);
|
super(source, name, dataType, qualifier, nullability, id, synthetic);
|
||||||
this.literal = literal;
|
this.literal = literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ public class LiteralAttribute extends TypedAttribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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) {
|
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
|
@Override
|
||||||
|
|
|
@ -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
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class ScalarSubquery extends SubQueryExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return true;
|
return Nullability.TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,9 @@ public abstract class TypedAttribute extends Attribute {
|
||||||
|
|
||||||
private final DataType dataType;
|
private final DataType dataType;
|
||||||
|
|
||||||
protected TypedAttribute(Source source, String name, DataType dataType) {
|
protected TypedAttribute(Source source, String name, DataType dataType, String qualifier, Nullability nullability,
|
||||||
this(source, name, dataType, null, true, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TypedAttribute(Source source, String name, DataType dataType, String qualifier, boolean nullable,
|
|
||||||
ExpressionId id, boolean synthetic) {
|
ExpressionId id, boolean synthetic) {
|
||||||
super(source, name, qualifier, nullable, id, synthetic);
|
super(source, name, qualifier, nullability, id, synthetic);
|
||||||
this.dataType = dataType;
|
this.dataType = dataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ public abstract class UnaryExpression extends Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return child.nullable();
|
return child.nullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class UnresolvedAlias extends UnresolvedNamedExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
throw new UnresolvedException("nullable", this);
|
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.Unresolvable;
|
||||||
import org.elasticsearch.xpack.sql.capabilities.UnresolvedException;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
import org.elasticsearch.xpack.sql.util.CollectionUtils;
|
import org.elasticsearch.xpack.sql.util.CollectionUtils;
|
||||||
|
|
||||||
|
@ -65,7 +65,8 @@ public class UnresolvedAttribute extends Attribute implements Unresolvable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class UnresolvedStar extends UnresolvedNamedExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
throw new UnresolvedException("nullable", this);
|
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.ExpressionId;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
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.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.util.StringUtils;
|
import org.elasticsearch.xpack.sql.util.StringUtils;
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ public abstract class Function extends NamedExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return Expressions.nullable(children());
|
return Expressions.nullable(children());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.function;
|
package org.elasticsearch.xpack.sql.expression.function;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
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.expression.TypedAttribute;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
@ -16,9 +17,9 @@ public abstract class FunctionAttribute extends TypedAttribute {
|
||||||
|
|
||||||
private final String functionId;
|
private final String functionId;
|
||||||
|
|
||||||
protected FunctionAttribute(Source source, String name, DataType dataType, String qualifier, boolean nullable, ExpressionId id,
|
protected FunctionAttribute(Source source, String name, DataType dataType, String qualifier, Nullability nullability,
|
||||||
boolean synthetic, String functionId) {
|
ExpressionId id, boolean synthetic, String functionId) {
|
||||||
super(source, name, dataType, qualifier, nullable, id, synthetic);
|
super(source, name, dataType, qualifier, nullability, id, synthetic);
|
||||||
this.functionId = functionId;
|
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.Attribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
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.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.ScorePipe;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
|
||||||
|
import static org.elasticsearch.xpack.sql.expression.Nullability.FALSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link Attribute} that represents Elasticsearch's {@code _score}.
|
* {@link Attribute} that represents Elasticsearch's {@code _score}.
|
||||||
*/
|
*/
|
||||||
|
@ -21,15 +24,15 @@ public class ScoreAttribute extends FunctionAttribute {
|
||||||
* Constructor for normal use.
|
* Constructor for normal use.
|
||||||
*/
|
*/
|
||||||
public ScoreAttribute(Source source) {
|
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()}
|
* 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) {
|
boolean synthetic) {
|
||||||
super(source, name, dataType, qualifier, nullable, id, synthetic, "SCORE");
|
super(source, name, dataType, qualifier, nullability, id, synthetic, "SCORE");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -38,8 +41,9 @@ public class ScoreAttribute extends FunctionAttribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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,
|
||||||
return new ScoreAttribute(source, name, dataType(), qualifier, nullable, id, synthetic);
|
ExpressionId id, boolean synthetic) {
|
||||||
|
return new ScoreAttribute(source, name, dataType(), qualifier, nullability, id, synthetic);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.Attribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
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.expression.gen.script.ScriptTemplate;
|
||||||
import org.elasticsearch.xpack.sql.session.Configuration;
|
import org.elasticsearch.xpack.sql.session.Configuration;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
|
@ -135,7 +136,7 @@ public class UnresolvedFunction extends Function implements Unresolvable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
throw new UnresolvedException("nullable", this);
|
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.Attribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
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.function.FunctionAttribute;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
|
||||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
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.DataType;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -21,12 +22,12 @@ public class AggregateFunctionAttribute extends FunctionAttribute {
|
||||||
|
|
||||||
AggregateFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id,
|
AggregateFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id,
|
||||||
String functionId, String propertyPath) {
|
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,
|
public AggregateFunctionAttribute(Source source, String name, DataType dataType, String qualifier,
|
||||||
boolean nullable, ExpressionId id, boolean synthetic, String functionId, String propertyPath) {
|
Nullability nullability, ExpressionId id, boolean synthetic, String functionId, String propertyPath) {
|
||||||
super(source, name, dataType, qualifier, nullable, id, synthetic, functionId);
|
super(source, name, dataType, qualifier, nullability, id, synthetic, functionId);
|
||||||
this.propertyPath = propertyPath;
|
this.propertyPath = propertyPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,14 +43,14 @@ public class AggregateFunctionAttribute extends FunctionAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Expression canonicalize() {
|
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
|
@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)
|
// 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
|
// 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) {
|
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.Attribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
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.function.FunctionAttribute;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
|
||||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
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.DataType;
|
||||||
|
|
||||||
public class GroupingFunctionAttribute extends FunctionAttribute {
|
public class GroupingFunctionAttribute extends FunctionAttribute {
|
||||||
|
|
||||||
GroupingFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id, String functionId) {
|
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,
|
public GroupingFunctionAttribute(Source source, String name, DataType dataType, String qualifier,
|
||||||
boolean nullable, ExpressionId id, boolean synthetic, String functionId) {
|
Nullability nullability, ExpressionId id, boolean synthetic, String functionId) {
|
||||||
super(source, name, dataType, qualifier, nullable, id, synthetic, functionId);
|
super(source, name, dataType, qualifier, nullability, id, synthetic, functionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -32,14 +33,15 @@ public class GroupingFunctionAttribute extends FunctionAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Expression canonicalize() {
|
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
|
@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)
|
// 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
|
// 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) {
|
public GroupingFunctionAttribute withFunctionId(String functionId, String propertyPath) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.function.scalar;
|
package org.elasticsearch.xpack.sql.expression.function.scalar;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.processor.Processor;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
|
@ -62,8 +63,11 @@ public class Cast extends UnaryScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return field().nullable() || DataTypes.isNull(from());
|
if (DataTypes.isNull(from())) {
|
||||||
|
return Nullability.TRUE;
|
||||||
|
}
|
||||||
|
return field().nullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.function.scalar;
|
package org.elasticsearch.xpack.sql.expression.function.scalar;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.expression.gen.script.ScriptTemplate;
|
||||||
import org.elasticsearch.xpack.sql.session.Configuration;
|
import org.elasticsearch.xpack.sql.session.Configuration;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
|
@ -42,8 +43,8 @@ public abstract class ConfigurationFunction extends ScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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.Attribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.ExpressionId;
|
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.function.FunctionAttribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
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.NodeInfo;
|
||||||
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -25,13 +26,13 @@ public class ScalarFunctionAttribute extends FunctionAttribute {
|
||||||
|
|
||||||
ScalarFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id,
|
ScalarFunctionAttribute(Source source, String name, DataType dataType, ExpressionId id,
|
||||||
String functionId, ScriptTemplate script, Expression orderBy, Pipe processorDef) {
|
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,
|
public ScalarFunctionAttribute(Source source, String name, DataType dataType, String qualifier,
|
||||||
boolean nullable, ExpressionId id, boolean synthetic, String functionId, ScriptTemplate script,
|
Nullability nullability, ExpressionId id, boolean synthetic, String functionId, ScriptTemplate script,
|
||||||
Expression orderBy, Pipe pipe) {
|
Expression orderBy, Pipe pipe) {
|
||||||
super(source, name, dataType, qualifier, nullable, id, synthetic, functionId);
|
super(source, name, dataType, qualifier, nullability, id, synthetic, functionId);
|
||||||
|
|
||||||
this.script = script;
|
this.script = script;
|
||||||
this.orderBy = orderBy;
|
this.orderBy = orderBy;
|
||||||
|
@ -60,14 +61,15 @@ public class ScalarFunctionAttribute extends FunctionAttribute {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Expression canonicalize() {
|
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);
|
functionId(), script, orderBy, pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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,
|
||||||
return new ScalarFunctionAttribute(source, name, dataType(), qualifier, nullable, id, synthetic,
|
ExpressionId id, boolean synthetic) {
|
||||||
functionId(), script, orderBy, pipe);
|
return new ScalarFunctionAttribute(source, name, dataType(), qualifier, nullability,
|
||||||
|
id, synthetic, functionId(), script, orderBy, pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
import org.elasticsearch.xpack.sql.expression.Expressions.ParamOrdinal;
|
||||||
import org.elasticsearch.xpack.sql.expression.FieldAttribute;
|
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.function.scalar.BinaryScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||||
|
@ -50,8 +51,8 @@ public class Concat extends BinaryScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.elasticsearch.xpack.sql.expression.predicate;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
import org.elasticsearch.xpack.sql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
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.function.scalar.ScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
|
import org.elasticsearch.xpack.sql.expression.gen.script.Params;
|
||||||
|
@ -119,8 +120,8 @@ public class Range extends ScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return value.nullable() && lower.nullable() && upper.nullable();
|
return Nullability.and(value.nullable(), lower.nullable(), upper.nullable());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
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.expression.function.scalar.ScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
@ -36,7 +37,7 @@ public abstract class ConditionalFunction extends ScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
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.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
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.pipeline.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
|
import org.elasticsearch.xpack.sql.expression.gen.script.ParamsBuilder;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||||
|
@ -57,8 +58,8 @@ public class NullIf extends ConditionalFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return true;
|
return Nullability.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.predicate.fulltext;
|
package org.elasticsearch.xpack.sql.expression.predicate.fulltext;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.type.DataType;
|
import org.elasticsearch.xpack.sql.type.DataType;
|
||||||
|
|
||||||
|
@ -56,8 +57,8 @@ public abstract class FullTextPredicate extends Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
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.pipeline.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
import org.elasticsearch.xpack.sql.expression.predicate.BinaryOperator;
|
||||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
import org.elasticsearch.xpack.sql.expression.predicate.logical.BinaryLogicProcessor.BinaryLogicOperation;
|
||||||
|
@ -35,8 +36,8 @@ public abstract class BinaryLogic extends BinaryOperator<Boolean, Boolean, Boole
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
// Cannot fold null due to 3vl, constant folding will do any possible folding.
|
// 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;
|
package org.elasticsearch.xpack.sql.expression.predicate.nulls;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.function.scalar.UnaryScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
||||||
|
@ -48,8 +49,8 @@ public class IsNotNull extends UnaryScalarFunction implements Negatable<UnarySca
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.predicate.nulls;
|
package org.elasticsearch.xpack.sql.expression.predicate.nulls;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.function.scalar.UnaryScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
import org.elasticsearch.xpack.sql.expression.gen.script.Scripts;
|
||||||
|
@ -48,8 +49,8 @@ public class IsNull extends UnaryScalarFunction implements Negatable<UnaryScalar
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.Expression;
|
||||||
import org.elasticsearch.xpack.sql.expression.Expressions;
|
import org.elasticsearch.xpack.sql.expression.Expressions;
|
||||||
import org.elasticsearch.xpack.sql.expression.Foldables;
|
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.function.scalar.ScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
import org.elasticsearch.xpack.sql.expression.gen.pipeline.Pipe;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
import org.elasticsearch.xpack.sql.expression.gen.script.ScriptTemplate;
|
||||||
|
@ -64,8 +65,8 @@ public class In extends ScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
package org.elasticsearch.xpack.sql.expression.predicate.operator.comparison;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.expression.predicate.operator.comparison.BinaryComparisonProcessor.BinaryComparisonOperation;
|
||||||
import org.elasticsearch.xpack.sql.tree.Source;
|
import org.elasticsearch.xpack.sql.tree.Source;
|
||||||
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
import org.elasticsearch.xpack.sql.tree.NodeInfo;
|
||||||
|
@ -35,7 +36,7 @@ public class NullEquals extends BinaryComparison {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.elasticsearch.xpack.sql.expression.predicate.regex;
|
package org.elasticsearch.xpack.sql.expression.predicate.regex;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.sql.expression.Expression;
|
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.function.scalar.UnaryScalarFunction;
|
||||||
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
import org.elasticsearch.xpack.sql.expression.gen.processor.Processor;
|
||||||
import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.RegexOperation;
|
import org.elasticsearch.xpack.sql.expression.predicate.regex.RegexProcessor.RegexOperation;
|
||||||
|
@ -28,8 +29,11 @@ public abstract class RegexMatch extends UnaryScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return field().nullable() && pattern != null;
|
if (pattern == null) {
|
||||||
|
return Nullability.TRUE;
|
||||||
|
}
|
||||||
|
return field().nullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.FieldAttribute;
|
||||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
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;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.Function;
|
import org.elasticsearch.xpack.sql.expression.function.Function;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute;
|
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.Range;
|
||||||
import org.elasticsearch.xpack.sql.expression.predicate.conditional.ArbitraryConditionalFunction;
|
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.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.And;
|
||||||
import org.elasticsearch.xpack.sql.expression.predicate.logical.Not;
|
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.logical.Or;
|
||||||
|
@ -1097,12 +1097,12 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||||
@Override
|
@Override
|
||||||
protected Expression rule(Expression e) {
|
protected Expression rule(Expression e) {
|
||||||
if (e instanceof IsNotNull) {
|
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);
|
return new Literal(e.source(), Expressions.name(e), Boolean.TRUE, DataType.BOOLEAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (e instanceof IsNull) {
|
} 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);
|
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);
|
return Literal.of(in, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (e instanceof NullIf) {
|
} else if (e.nullable() == Nullability.TRUE && Expressions.anyMatch(e.children(), Expressions::isNull)) {
|
||||||
return e;
|
|
||||||
|
|
||||||
} else if (e.nullable() && Expressions.anyMatch(e.children(), Expressions::isNull)) {
|
|
||||||
return Literal.of(e, null);
|
return Literal.of(e, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1314,7 +1311,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||||
|
|
||||||
// true for equality
|
// true for equality
|
||||||
if (bc instanceof Equals || bc instanceof GreaterThanOrEqual || bc instanceof LessThanOrEqual) {
|
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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1329,7 +1326,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||||
|
|
||||||
// false for equality
|
// false for equality
|
||||||
if (bc instanceof NotEquals || bc instanceof GreaterThan || bc instanceof LessThan) {
|
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;
|
return FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,17 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.sql.plan.logical;
|
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.List;
|
||||||
import java.util.Objects;
|
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 java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.sql.util.CollectionUtils.combine;
|
import static org.elasticsearch.xpack.sql.util.CollectionUtils.combine;
|
||||||
|
|
||||||
public class Join extends BinaryPlan {
|
public class Join extends BinaryPlan {
|
||||||
|
@ -78,7 +78,7 @@ public class Join extends BinaryPlan {
|
||||||
|
|
||||||
private static List<Attribute> makeNullable(List<Attribute> output) {
|
private static List<Attribute> makeNullable(List<Attribute> output) {
|
||||||
return output.stream()
|
return output.stream()
|
||||||
.map(a -> a.withNullability(true))
|
.map(a -> a.withNullability(Nullability.TRUE))
|
||||||
.collect(toList());
|
.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.Foldables;
|
||||||
import org.elasticsearch.xpack.sql.expression.Literal;
|
import org.elasticsearch.xpack.sql.expression.Literal;
|
||||||
import org.elasticsearch.xpack.sql.expression.NamedExpression;
|
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;
|
||||||
import org.elasticsearch.xpack.sql.expression.Order.OrderDirection;
|
import org.elasticsearch.xpack.sql.expression.Order.OrderDirection;
|
||||||
import org.elasticsearch.xpack.sql.expression.function.Function;
|
import org.elasticsearch.xpack.sql.expression.function.Function;
|
||||||
|
@ -134,8 +135,8 @@ public class OptimizerTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean nullable() {
|
public Nullability nullable() {
|
||||||
return false;
|
return Nullability.FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -393,6 +394,8 @@ public class OptimizerTests extends ESTestCase {
|
||||||
return ((Literal) new ConstantFolding().rule(b)).value();
|
return ((Literal) new ConstantFolding().rule(b)).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Null folding
|
||||||
|
|
||||||
public void testNullFoldingIsNull() {
|
public void testNullFoldingIsNull() {
|
||||||
FoldNull foldNull = new FoldNull();
|
FoldNull foldNull = new FoldNull();
|
||||||
assertEquals(true, foldNull.rule(new IsNull(EMPTY, Literal.NULL)).fold());
|
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")));
|
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() {
|
public void testSimplifyCoalesceNulls() {
|
||||||
Expression e = new SimplifyConditional().rule(new Coalesce(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
Expression e = new SimplifyConditional().rule(new Coalesce(EMPTY, asList(Literal.NULL, Literal.NULL)));
|
||||||
assertEquals(Coalesce.class, e.getClass());
|
assertEquals(Coalesce.class, e.getClass());
|
||||||
|
|
Loading…
Reference in New Issue