From 4eabe9a787279cd62a23aac749b84f53a9940e4b Mon Sep 17 00:00:00 2001 From: Stamatis Zampetakis Date: Mon, 10 Nov 2025 16:47:56 +0100 Subject: [PATCH] [CALCITE-4099] Unify Strong.Policy and NullPolicy 1. Deprecate Strong.Policy in favor of NullPolicy 2. Add NullPolicy.NEVER for describing operators that never return null 3. Replace Strong.Policy with NullPolicy using the following mapping: Strong.Policy.ANY -> NullPolicy.STRICT Strong.Policy.AS_IS -> NullPolicy.NONE Strong.Policy.CUSTOM -> NullPolicy.NONE Strong.Policy.NOT_NULL -> NullPolicy.NEVER essentially AS_IS and CUSTOM are now handled in the same way. 4. Deprecate APIs using/returning Strong.Policy 5. Use NullPolicy mapping from RexImpTable as the primary source of truth 6. Modify NullPolicy for IsXx, NOT, and CAST operators in RexImpTable to be aligned with desired behavior 7. Define fallback mapping using SqlKind in Strong.Policy for projects that define their own operators (most likely will drop that) 8. Hardcode policy for some operators not defined in RexImpTable failing the assertion in SqlOperator#getNullPolicy 9. Update plan/sql in JdbcAdapterTest#testUnknownColumn which changes from LEFT JOIN to INNER JOIN due FilterJoinRule and in particular RelOptUtil#simplifyJoin since the CONCAT operator is now defined as STRICT so we can infer that the filter would reject nulls and thus we are able to simplify. --- .../adapter/enumerable/NullPolicy.java | 4 ++ .../adapter/enumerable/RexImpTable.java | 41 +++++++++---- .../java/org/apache/calcite/plan/Strong.java | 59 ++++++++++++++++--- .../org/apache/calcite/rex/RexSimplify.java | 34 ++++++----- .../org/apache/calcite/sql/SqlOperator.java | 22 +++++++ .../calcite/sql/fun/SqlStdOperatorTable.java | 25 +++++++- .../apache/calcite/rex/RexProgramTest.java | 14 ++--- .../apache/calcite/test/JdbcAdapterTest.java | 26 ++++---- .../apache/calcite/test/SqlOperatorTest.java | 6 +- 9 files changed, 167 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java index 4000f97620b8..06cd896d4aa6 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/NullPolicy.java @@ -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 } diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java index ca8f93feb9fe..508c5c36ad72 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java @@ -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()); @@ -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()); } @@ -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, @@ -4392,6 +4406,7 @@ private List unboxIfNecessary(final List argValueList) { switch (nullPolicy) { case STRICT: case SEMI_STRICT: + case NEVER: return Util.transform(argValueList, AbstractRexCallImplementor::unboxExpression); case ARG0: @@ -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, @@ -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 argValueList) { @@ -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 argValueList) { @@ -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 argValueList) { @@ -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 argValueList) { @@ -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 argValueList) { @@ -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, @@ -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 argValueList) { diff --git a/core/src/main/java/org/apache/calcite/plan/Strong.java b/core/src/main/java/org/apache/calcite/plan/Strong.java index b92c98f56086..0c9ea6c7ce77 100644 --- a/core/src/main/java/org/apache/calcite/plan/Strong.java +++ b/core/src/main/java/org/apache/calcite/plan/Strong.java @@ -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; @@ -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; @@ -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()); @@ -127,6 +131,7 @@ 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(); @@ -134,6 +139,20 @@ public static Policy policy(SqlOperator operator) { 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. * @@ -207,14 +226,17 @@ private boolean anyNotTrue(List 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()) { @@ -383,6 +405,7 @@ private static Map 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. */ @@ -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; + } + } } } diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java index 4a9677c27de9..f607f813f31d 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java @@ -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; @@ -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 operands = new ArrayList<>(); @@ -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; } } @@ -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 operands = new ArrayList<>(); @@ -1230,7 +1226,6 @@ private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) { } } return RexUtil.composeDisjunction(rexBuilder, operands, false); - case AS_IS: default: return null; } @@ -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 operands = ((RexCall) rexNode).getOperands(); if (rexNode.getType().isNullable()) { assert operands.stream() @@ -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. diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java index dc8f09770997..c53905b5992c 100644 --- a/core/src/main/java/org/apache/calcite/sql/SqlOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/SqlOperator.java @@ -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; @@ -1033,10 +1035,30 @@ public void acceptCall( * @see Strong */ @Pure + @Deprecated public @Nullable Supplier 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. * diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java index 074c3103f299..717908f99414 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.sql.fun; +import org.apache.calcite.adapter.enumerable.NullPolicy; import org.apache.calcite.avatica.util.TimeUnit; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.sql.SqlAggFunction; @@ -415,7 +416,13 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { true, ReturnTypes.BOOLEAN, InferTypes.FIRST_KNOWN, - OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED); + OperandTypes.COMPARABLE_UNORDERED_COMPARABLE_UNORDERED) { + @Override public NullPolicy getNullPolicy() { + // Missing in RexImpTable mapping but present in Strong.Policy + // Override here till we decide where the mapping will leave. + return NullPolicy.NEVER; + } + }; /** * IS NOT DISTINCT FROM operator. Is equivalent to NOT(x @@ -832,7 +839,13 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { 28, ReturnTypes.BOOLEAN_NOT_NULL, InferTypes.BOOLEAN, - OperandTypes.BOOLEAN); + OperandTypes.BOOLEAN) { + @Override public NullPolicy getNullPolicy() { + // Missing in RexImpTable mapping but present in Strong.Policy + // Override here till we decide where the mapping will leave. + return NullPolicy.NEVER; + } + }; public static final SqlPostfixOperator IS_UNKNOWN = new SqlPostfixOperator( @@ -841,7 +854,13 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable { 28, ReturnTypes.BOOLEAN_NOT_NULL, InferTypes.BOOLEAN, - OperandTypes.BOOLEAN); + OperandTypes.BOOLEAN) { + @Override public NullPolicy getNullPolicy() { + // Missing in RexImpTable mapping but present in Strong.Policy + // Override here till we decide where the mapping will leave. + return NullPolicy.NEVER; + } + }; public static final SqlPostfixOperator IS_A_SET = new SqlPostfixOperator( diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java index 558cedcd42ac..273f9b1bf3d6 100644 --- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.rex; +import org.apache.calcite.adapter.enumerable.NullPolicy; import org.apache.calcite.avatica.util.ByteString; import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptUtil; @@ -67,7 +68,6 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; -import java.util.function.Supplier; import static org.apache.calcite.test.Matchers.isRangeSet; @@ -4170,16 +4170,16 @@ private void checkSarg(String message, Sarg sarg, /** An operator that overrides the {@link #getStrongPolicyInference} * method. */ private static class SqlSpecialOperatorWithPolicy extends SqlSpecialOperator { - private final Strong.Policy policy; + private final NullPolicy policy; private SqlSpecialOperatorWithPolicy(String name, SqlKind kind, int prec, boolean leftAssoc, SqlReturnTypeInference returnTypeInference, SqlOperandTypeInference operandTypeInference, - SqlOperandTypeChecker operandTypeChecker, Strong.Policy policy) { + SqlOperandTypeChecker operandTypeChecker, NullPolicy policy) { super(name, kind, prec, leftAssoc, returnTypeInference, operandTypeInference, operandTypeChecker); this.policy = policy; } - @Override public Supplier getStrongPolicyInference() { - return () -> policy; + @Override public NullPolicy getNullPolicy() { + return policy; } } @@ -4197,7 +4197,7 @@ private SqlSpecialOperatorWithPolicy(String name, SqlKind kind, int prec, boolea final SqlOperator opPolicyAsIs = new SqlSpecialOperatorWithPolicy("OP2", SqlKind.OTHER_FUNCTION, 0, - false, ReturnTypes.BOOLEAN, null, null, Strong.Policy.AS_IS) { + false, ReturnTypes.BOOLEAN, null, null, NullPolicy.NONE) { }; // Operator with Strong.Policy.AS_IS but not safe: no simplification can be made checkSimplifyUnchanged(rexBuilder.makeCall(opPolicyAsIs, vInt())); @@ -4206,7 +4206,7 @@ private SqlSpecialOperatorWithPolicy(String name, SqlKind kind, int prec, boolea final SqlOperator opPolicyAny = new SqlSpecialOperatorWithPolicy("OP3", SqlKind.OTHER_FUNCTION, 0, - false, ReturnTypes.BOOLEAN, null, null, Strong.Policy.ANY) { + false, ReturnTypes.BOOLEAN, null, null, NullPolicy.STRICT) { @Override public Boolean isSafeOperator() { return true; } diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java index 51e2aca144b9..03e0c5c387ea 100644 --- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java +++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java @@ -1479,25 +1479,25 @@ private LockWrapper exclusiveCleanDb(Connection c) throws SQLException { + "WHERE\n" + " \"content-owner\" IN (?)") .planHasSql("SELECT " - + "\"t2\".\"DNAME\" AS \"content-format-owner\", " - + "\"t2\".\"DNAME0\" || ' ' AS \"content-owner\"\n" - + "FROM (SELECT \"t\".\"DEPTNO\" AS \"DEPTNO\", " + + "\"t3\".\"DNAME\" AS \"content-format-owner\", " + + "\"t3\".\"DNAME0\" || ' ' AS \"content-owner\"\n" + + "FROM (SELECT \"t\".\"DEPTNO\", " + "\"t0\".\"DEPTNO\" AS \"DEPTNO0\", " - + "\"t0\".\"DNAME\" AS \"DNAME\", " - + "\"t1\".\"DEPTNO\" AS \"DEPTNO1\", " - + "\"t1\".\"DNAME\" AS \"DNAME0\"\n" + + "\"t0\".\"DNAME\", " + + "CAST(\"t2\".\"DEPTNO\" AS TINYINT) AS \"DEPTNO1\", " + + "\"t2\".\"DNAME\" AS \"DNAME0\"\n" + "FROM (SELECT \"DEPTNO\"\n" + "FROM \"SCOTT\".\"EMP\") AS \"t\"\n" + "LEFT JOIN (SELECT \"DEPTNO\", \"DNAME\"\n" + "FROM \"SCOTT\".\"DEPT\") AS \"t0\" ON \"t\".\"DEPTNO\" = \"t0\".\"DEPTNO\"\n" - + "LEFT JOIN (SELECT \"DEPTNO\", \"DNAME\"\n" - + "FROM \"SCOTT\".\"DEPT\") AS \"t1\" " - + "ON \"t\".\"DEPTNO\" = \"t1\".\"DEPTNO\"\n" - + "WHERE \"t1\".\"DNAME\" || ' ' = ?) AS \"t2\"\n" + + "INNER JOIN (SELECT \"DEPTNO\", \"DNAME\"\n" + + "FROM \"SCOTT\".\"DEPT\"\n" + + "WHERE \"DNAME\" || ' ' = ?) AS \"t2\" " + + "ON \"t\".\"DEPTNO\" = \"t2\".\"DEPTNO\") AS \"t3\"\n" + "LEFT JOIN (SELECT \"DEPTNO\"\n" - + "FROM \"SCOTT\".\"EMP\") AS \"t3\" " - + "ON \"t2\".\"DEPTNO\" = \"t3\".\"DEPTNO\"\n" - + "GROUP BY \"t2\".\"DNAME\", \"t2\".\"DNAME0\"") + + "FROM \"SCOTT\".\"EMP\") AS \"t4\" " + + "ON \"t3\".\"DEPTNO\" = \"t4\".\"DEPTNO\"\n" + + "GROUP BY \"t3\".\"DNAME\", \"t3\".\"DNAME0\"") .runs(); } diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java index b11a96845827..c729d5130891 100644 --- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java +++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.test; +import org.apache.calcite.adapter.enumerable.NullPolicy; import org.apache.calcite.avatica.util.ByteString; import org.apache.calcite.avatica.util.DateTimeUtils; import org.apache.calcite.config.CalciteConnectionProperty; @@ -23,7 +24,6 @@ import org.apache.calcite.linq4j.function.Function1; import org.apache.calcite.linq4j.function.Function2; import org.apache.calcite.linq4j.tree.Types; -import org.apache.calcite.plan.Strong; import org.apache.calcite.rel.type.DelegatingTypeSystem; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; @@ -17581,9 +17581,9 @@ void testCastTruncates(CastType castType, SqlOperatorFixture f) { || s.matches("MOD\\(.*, 0\\)")) { continue; } - final Strong.Policy policy = Strong.policy(op); + final NullPolicy policy = op.getNullPolicy(); try { - if (nullCount > 0 && policy == Strong.Policy.ANY) { + if (nullCount > 0 && policy == NullPolicy.STRICT) { f.checkNull(s); } else { final String query;