Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,9 @@ public enum NullPolicy {
ANY,
/** If the first argument is null, return null. */
ARG0,
/** Never returns null no matter the values of the arguments. */
NEVER,
/** Returns null under certain conditions that depend entirely on the operator/function.
* Operators using this policy have custom rules for when to return null.*/
NONE
}
Original file line number Diff line number Diff line change
Expand Up @@ -1138,9 +1138,9 @@ void populate2() {
define(NOT_SUBMULTISET_OF, NotImplementor.of(subMultisetImplementor));

define(COALESCE, new CoalesceImplementor());
define(CAST, new CastImplementor());
define(SAFE_CAST, new CastImplementor());
define(TRY_CAST, new CastImplementor());
define(CAST, new CastImplementor(NullPolicy.STRICT));
define(SAFE_CAST, new CastImplementor(NullPolicy.SEMI_STRICT));
define(TRY_CAST, new CastImplementor(NullPolicy.SEMI_STRICT));

define(REINTERPRET, new ReinterpretImplementor());
define(CONVERT, new ConvertImplementor());
Expand Down Expand Up @@ -1539,6 +1539,20 @@ public TableFunctionCallImplementor get(final SqlWindowTableFunction operator) {
}
}

/**
* Returns the NullPolicy for the specified operator.
*
* @param operator the operator
* @return the NullPolicy for the specified operator, or null if a policy is undefined.
*/
public @Nullable NullPolicy getPolicy(SqlOperator operator) {
final RexCallImplementor implementor = get(operator);
if (implementor instanceof AbstractRexCallImplementor) {
return ((AbstractRexCallImplementor) implementor).nullPolicy;
}
return null;
}

static Expression optimize(Expression expression) {
return expression.accept(new OptimizeShuttle());
}
Expand Down Expand Up @@ -3644,8 +3658,8 @@ private static Expression implementRecurse(RexToLixTranslator translator,

/** Implementor for the SQL {@code CAST} operator. */
private static class CastImplementor extends AbstractRexCallImplementor {
CastImplementor() {
super("cast", NullPolicy.STRICT, false);
CastImplementor(NullPolicy nullPolicy) {
super("cast", nullPolicy, false);
}

@Override Expression implementSafe(final RexToLixTranslator translator,
Expand Down Expand Up @@ -4392,6 +4406,7 @@ private List<Expression> unboxIfNecessary(final List<Expression> argValueList) {
switch (nullPolicy) {
case STRICT:
case SEMI_STRICT:
case NEVER:
return Util.transform(argValueList,
AbstractRexCallImplementor::unboxExpression);
case ARG0:
Expand Down Expand Up @@ -4544,7 +4559,7 @@ private static class LogicalOrImplementor extends AbstractRexCallImplementor {
*/
private static class LogicalNotImplementor extends AbstractRexCallImplementor {
LogicalNotImplementor() {
super("logical_not", NullPolicy.NONE, true);
super("logical_not", NullPolicy.STRICT, true);
}

@Override Expression implementSafe(final RexToLixTranslator translator,
Expand Down Expand Up @@ -4714,7 +4729,7 @@ private static class PiImplementor extends AbstractRexCallImplementor {
/** Implementor for the {@code IS FALSE} SQL operator. */
private static class IsFalseImplementor extends AbstractRexCallImplementor {
IsFalseImplementor() {
super("is_false", NullPolicy.STRICT, false);
super("is_false", NullPolicy.NEVER, false);
}

@Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
Expand All @@ -4730,7 +4745,7 @@ private static class IsFalseImplementor extends AbstractRexCallImplementor {
/** Implementor for the {@code IS NOT FALSE} SQL operator. */
private static class IsNotFalseImplementor extends AbstractRexCallImplementor {
IsNotFalseImplementor() {
super("is_not_false", NullPolicy.STRICT, false);
super("is_not_false", NullPolicy.NEVER, false);
}

@Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
Expand All @@ -4746,7 +4761,7 @@ private static class IsNotFalseImplementor extends AbstractRexCallImplementor {
/** Implementor for the {@code IS NOT NULL} SQL operator. */
private static class IsNotNullImplementor extends AbstractRexCallImplementor {
IsNotNullImplementor() {
super("is_not_null", NullPolicy.STRICT, false);
super("is_not_null", NullPolicy.NEVER, false);
}

@Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
Expand All @@ -4762,7 +4777,7 @@ private static class IsNotNullImplementor extends AbstractRexCallImplementor {
/** Implementor for the {@code IS NOT TRUE} SQL operator. */
private static class IsNotTrueImplementor extends AbstractRexCallImplementor {
IsNotTrueImplementor() {
super("is_not_true", NullPolicy.STRICT, false);
super("is_not_true", NullPolicy.NEVER, false);
}

@Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
Expand All @@ -4778,7 +4793,7 @@ private static class IsNotTrueImplementor extends AbstractRexCallImplementor {
/** Implementor for the {@code IS NULL} SQL operator. */
private static class IsNullImplementor extends AbstractRexCallImplementor {
IsNullImplementor() {
super("is_null", NullPolicy.STRICT, false);
super("is_null", NullPolicy.NEVER, false);
}

@Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
Expand All @@ -4794,7 +4809,7 @@ private static class IsNullImplementor extends AbstractRexCallImplementor {
/** Implementor for the {@code IS NOT DISTINCT FROM} SQL operator. */
private static class IsNotDistinctFromImplementor extends AbstractRexCallImplementor {
IsNotDistinctFromImplementor() {
super("is_not_distinct_from", NullPolicy.NONE, false);
super("is_not_distinct_from", NullPolicy.NEVER, false);
}

@Override public RexToLixTranslator.Result implement(final RexToLixTranslator translator,
Expand Down Expand Up @@ -4840,7 +4855,7 @@ private static class IsNotDistinctFromImplementor extends AbstractRexCallImpleme
/** Implementor for the {@code IS TRUE} SQL operator. */
private static class IsTrueImplementor extends AbstractRexCallImplementor {
IsTrueImplementor() {
super("is_true", NullPolicy.STRICT, false);
super("is_true", NullPolicy.NEVER, false);
}

@Override Expression getIfTrue(Type type, final List<Expression> argValueList) {
Expand Down
59 changes: 50 additions & 9 deletions core/src/main/java/org/apache/calcite/plan/Strong.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.calcite.plan;

import org.apache.calcite.adapter.enumerable.NullPolicy;
import org.apache.calcite.rex.RexCall;
import org.apache.calcite.rex.RexFieldAccess;
import org.apache.calcite.rex.RexInputRef;
Expand All @@ -35,6 +36,8 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
Expand Down Expand Up @@ -116,6 +119,7 @@ public static Policy policy(SqlKind kind) {
* Returns how to deduce whether a particular {@link RexNode} expression is null,
* given whether its arguments are null.
*/
@Deprecated
public static Policy policy(RexNode rexNode) {
if (rexNode instanceof RexCall) {
return policy(((RexCall) rexNode).getOperator());
Expand All @@ -127,13 +131,28 @@ public static Policy policy(RexNode rexNode) {
* Returns how to deduce whether a particular {@link SqlOperator} expression is null,
* given whether its arguments are null.
*/
@Deprecated
public static Policy policy(SqlOperator operator) {
if (operator.getStrongPolicyInference() != null) {
return operator.getStrongPolicyInference().get();
}
return MAP.getOrDefault(operator.getKind(), Policy.AS_IS);
}

/**
* Returns the {@link NullPolicy} for a given {@link SqlKind}.
*
* @param kind SqlKind
* @return NullPolicy or null if not defined
*/
public static @Nullable NullPolicy nullPolicy(SqlKind kind) {
Policy p = MAP.get(kind);
if (p != null) {
return p.nullPolicy();
}
return null;
}

/**
* Returns whether a given expression is strong.
*
Expand Down Expand Up @@ -207,14 +226,17 @@ private boolean anyNotTrue(List<RexNode> operands) {
* expressions, and you may override methods to test hypotheses such as
* "if {@code x} is null, is {@code x + y} null? */
public boolean isNull(RexNode node) {
final Policy policy = policy(node);
switch (policy) {
case NOT_NULL:
return false;
case ANY:
return anyNull(((RexCall) node).getOperands());
default:
break;
if (node instanceof RexCall) {
RexCall call = (RexCall) node;
final NullPolicy policy = call.getOperator().getNullPolicy();
switch (policy) {
case NEVER:
return false;
case STRICT:
return anyNull(call.getOperands());
default:
break;
}
}

switch (node.getKind()) {
Expand Down Expand Up @@ -383,6 +405,7 @@ private static Map<SqlKind, Policy> createPolicyMap() {

/** How whether an operator's operands are null affects whether a call to
* that operator evaluates to null. */
@Deprecated
public enum Policy {
/** This kind of expression is never null. No need to look at its arguments,
* if it has any. */
Expand All @@ -397,6 +420,24 @@ public enum Policy {
ANY,

/** This kind of expression may be null. There is no way to rewrite. */
AS_IS,
AS_IS;

/**
* Returns the corresponding {@link NullPolicy}.
*
* @return the corresponding NullPolicy
*/
NullPolicy nullPolicy() {
switch (this) {
case NOT_NULL:
return NullPolicy.NEVER;
case ANY:
return NullPolicy.STRICT;
case CUSTOM:
case AS_IS:
default:
return NullPolicy.NONE;
}
}
}
}
34 changes: 18 additions & 16 deletions core/src/main/java/org/apache/calcite/rex/RexSimplify.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.calcite.rex;

import org.apache.calcite.adapter.enumerable.NullPolicy;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.avatica.util.TimeUnitRange;
import org.apache.calcite.plan.RelOptPredicateList;
Expand Down Expand Up @@ -1158,10 +1159,10 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
if (hasCustomNullabilityRules(a.getKind())) {
return null;
}
switch (Strong.policy(a)) {
case NOT_NULL:
switch (policy(a)) {
case NEVER:
return rexBuilder.makeLiteral(true);
case ANY:
case STRICT:
// "f" is a strong operator, so "f(operand0, operand1) IS NOT NULL"
// simplifies to "operand0 IS NOT NULL AND operand1 IS NOT NULL"
final List<RexNode> operands = new ArrayList<>();
Expand All @@ -1177,16 +1178,11 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
}
}
return RexUtil.composeConjunction(rexBuilder, operands);
case CUSTOM:
default:
switch (a.getKind()) {
case LITERAL:
return rexBuilder.makeLiteral(!((RexLiteral) a).isNull());
default:
throw new AssertionError("every CUSTOM policy needs a handler, "
+ a.getKind());
}
case AS_IS:
default:
return null;
}
}
Expand All @@ -1213,10 +1209,10 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
if (hasCustomNullabilityRules(a.getKind())) {
return null;
}
switch (Strong.policy(a)) {
case NOT_NULL:
switch (policy(a)) {
case NEVER:
return rexBuilder.makeLiteral(false);
case ANY:
case STRICT:
// "f" is a strong operator, so "f(operand0, operand1) IS NULL" simplifies
// to "operand0 IS NULL OR operand1 IS NULL"
final List<RexNode> operands = new ArrayList<>();
Expand All @@ -1230,7 +1226,6 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
}
}
return RexUtil.composeDisjunction(rexBuilder, operands, false);
case AS_IS:
default:
return null;
}
Expand All @@ -1246,11 +1241,11 @@ private static void validateStrongPolicy(RexNode rexNode) {
if (hasCustomNullabilityRules(rexNode.getKind())) {
return;
}
switch (Strong.policy(rexNode)) {
case NOT_NULL:
switch (policy(rexNode)) {
case NEVER:
assert !rexNode.getType().isNullable();
break;
case ANY:
case STRICT:
List<RexNode> operands = ((RexCall) rexNode).getOperands();
if (rexNode.getType().isNullable()) {
assert operands.stream()
Expand All @@ -1267,6 +1262,13 @@ private static void validateStrongPolicy(RexNode rexNode) {
}
}

private static NullPolicy policy(RexNode node) {
if (node instanceof RexCall) {
return ((RexCall) node).getOperator().getNullPolicy();
}
return NullPolicy.NONE;
}

/**
* Returns {@code true} if specified {@link SqlKind} has custom nullability rules which
* depend not only on the nullability of input operands.
Expand Down
22 changes: 22 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package org.apache.calcite.sql;

import org.apache.calcite.adapter.enumerable.NullPolicy;
import org.apache.calcite.adapter.enumerable.RexImpTable;
import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.Strong;
import org.apache.calcite.rel.type.RelDataType;
Expand Down Expand Up @@ -1033,10 +1035,30 @@ public <R> void acceptCall(
* @see Strong
*/
@Pure
@Deprecated
public @Nullable Supplier<Strong.Policy> getStrongPolicyInference() {
return null;
}

/**
* Returns the null policy for this operator.
*
* @return the null policy for this operator
*/
public NullPolicy getNullPolicy() {
NullPolicy p = RexImpTable.INSTANCE.getPolicy(this);
if (p != null) {
return p;
}
p = Strong.nullPolicy(getKind());
if (p != null) {
assert p == NullPolicy.NONE
: p + " policy is only defined via SqlKind for " + this.getName();
return p;
}
return NullPolicy.NONE;
}

/**
* Returns whether this is a safe operator.
*
Expand Down
Loading
Loading