This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit b432756e2be9ad0557a56254550eb4438dd0efcf Author: Julian Hyde <[email protected]> AuthorDate: Fri Aug 2 17:59:01 2019 -0700 Deprecate RexBuilder.constantNull(), because it produces untyped NULL literals that make planning difficult We cannot deprecate RelBuilder.literal(null), but remove all uses of in internal code, for similar reasons. --- .../org/apache/calcite/rel/core/TableScan.java | 6 +- .../AggregateExpandDistinctAggregatesRule.java | 3 +- .../rel/rules/AggregateReduceFunctionsRule.java | 65 +++++------- .../calcite/rel/rules/AggregateValuesRule.java | 3 +- .../calcite/rel/rules/LoptOptimizeJoinRule.java | 3 +- .../calcite/rel/rules/SubQueryRemoveRule.java | 115 +++++++++++---------- .../apache/calcite/rel/rules/ValuesReduceRule.java | 11 +- .../java/org/apache/calcite/rex/RexBuilder.java | 8 +- .../sql2rel/NullInitializerExpressionFactory.java | 8 +- .../apache/calcite/sql2rel/RelDecorrelator.java | 25 ++--- .../sql2rel/RelStructuredTypeFlattener.java | 7 +- .../calcite/sql2rel/SqlNodeToRexConverterImpl.java | 4 +- .../calcite/sql2rel/StandardConvertletTable.java | 11 +- .../java/org/apache/calcite/tools/RelBuilder.java | 3 +- .../org/apache/calcite/test/RexProgramTest.java | 6 +- .../catalog/EmpInitializerExpressionFactory.java | 2 +- .../org/apache/calcite/test/RelOptRulesTest.xml | 32 +++--- core/src/test/resources/sql/blank.iq | 2 +- core/src/test/resources/sql/sub-query.iq | 4 +- 19 files changed, 148 insertions(+), 170 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java index d87715b..07dce01 100644 --- a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java +++ b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java @@ -152,11 +152,7 @@ public abstract class TableScan extends AbstractRelNode { // Project nulls for the extra fields. (Maybe a sub-class table has // extra fields, but we don't.) for (RelDataTypeField extraField : extraFields) { - exprList.add( - rexBuilder.ensureType( - extraField.getType(), - rexBuilder.constantNull(), - true)); + exprList.add(rexBuilder.makeNullLiteral(extraField.getType())); nameList.add(extraField.getName()); } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java index d773d80..1a75136 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java @@ -824,8 +824,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule { rexBuilder.makeCall(SqlStdOperatorTable.CASE, filterRef, argRef.left, rexBuilder.ensureType(argRef.left.getType(), - rexBuilder.makeCast(argRef.left.getType(), - rexBuilder.constantNull()), + rexBuilder.makeNullLiteral(argRef.left.getType()), true)); sourceOf.put(arg, projects.size()); projects.add(Pair.of(condition, "i$" + argRef.right)); diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java index b97a9bb..80e7ae7 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java @@ -475,7 +475,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule { return rexBuilder.makeCall(SqlStdOperatorTable.CASE, rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, countRef, rexBuilder.makeExactLiteral(BigDecimal.ZERO)), - rexBuilder.makeCast(sumZeroRef.getType(), rexBuilder.constantNull()), + rexBuilder.makeNullLiteral(sumZeroRef.getType()), sumZeroRef); } @@ -572,46 +572,18 @@ public class AggregateReduceFunctionsRule extends RelOptRule { aggCallMapping, ImmutableList.of(argOrdinalType)); - final RexNode avgSumSquaredArg = - rexBuilder.makeCall( - SqlStdOperatorTable.DIVIDE, sumSquaredArg, countArg); - - final RexNode diff = - rexBuilder.makeCall( - SqlStdOperatorTable.MINUS, - sumArgSquared, avgSumSquaredArg); - - final RexNode denominator; - if (biased) { - denominator = countArg; - } else { - final RexLiteral one = - rexBuilder.makeExactLiteral(BigDecimal.ONE); - final RexNode nul = - rexBuilder.makeCast(countArg.getType(), rexBuilder.constantNull()); - final RexNode countMinusOne = - rexBuilder.makeCall( - SqlStdOperatorTable.MINUS, countArg, one); - final RexNode countEqOne = - rexBuilder.makeCall( - SqlStdOperatorTable.EQUALS, countArg, one); - denominator = - rexBuilder.makeCall( - SqlStdOperatorTable.CASE, - countEqOne, nul, countMinusOne); - } + final RexNode div = divide(biased, rexBuilder, sumArgSquared, sumSquaredArg, + countArg); - final RexNode div = - rexBuilder.makeCall( - SqlStdOperatorTable.DIVIDE, diff, denominator); - - RexNode result = div; + final RexNode result; if (sqrt) { final RexNode half = rexBuilder.makeExactLiteral(new BigDecimal("0.5")); result = rexBuilder.makeCall( SqlStdOperatorTable.POWER, div, half); + } else { + result = div; } return rexBuilder.makeCast( @@ -750,7 +722,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule { ImmutableIntList.of(xIndex), ImmutableList.of(argXType), argXAndYNotNullFilterOrdinal); RexLiteral zero = rexBuilder.makeExactLiteral(BigDecimal.ZERO); - RexNode nul = rexBuilder.constantNull(); + RexNode nul = rexBuilder.makeNullLiteral(zero.getType()); final RexNode avgSumXSumY = rexBuilder.makeCall(SqlStdOperatorTable.CASE, rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, countArg, zero), nul, rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, sumXSumY, countArg)); @@ -803,21 +775,30 @@ public class AggregateReduceFunctionsRule extends RelOptRule { ImmutableIntList.of(argXOrdinal, argYOrdinal), ImmutableList.of(argXOrdinalType, argYOrdinalType), argXAndYNotNullFilterOrdinal); + final RexNode result = divide(biased, rexBuilder, sumXY, sumXSumY, countArg); + return rexBuilder.makeCast(oldCall.getType(), result); + } + + private RexNode divide(boolean biased, RexBuilder rexBuilder, RexNode sumXY, + RexNode sumXSumY, RexNode countArg) { final RexNode avgSumSquaredArg = rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, sumXSumY, countArg); - final RexNode diff = rexBuilder.makeCall(SqlStdOperatorTable.MINUS, sumXY, avgSumSquaredArg); + final RexNode diff = + rexBuilder.makeCall(SqlStdOperatorTable.MINUS, sumXY, avgSumSquaredArg); final RexNode denominator; if (biased) { denominator = countArg; } else { final RexLiteral one = rexBuilder.makeExactLiteral(BigDecimal.ONE); - final RexNode nul = rexBuilder.makeCast(countArg.getType(), rexBuilder.constantNull()); - final RexNode countMinusOne = rexBuilder.makeCall(SqlStdOperatorTable.MINUS, countArg, one); - final RexNode countEqOne = rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, countArg, one); - denominator = rexBuilder.makeCall(SqlStdOperatorTable.CASE, countEqOne, nul, countMinusOne); + final RexNode nul = rexBuilder.makeNullLiteral(countArg.getType()); + final RexNode countMinusOne = + rexBuilder.makeCall(SqlStdOperatorTable.MINUS, countArg, one); + final RexNode countEqOne = + rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, countArg, one); + denominator = rexBuilder.makeCall(SqlStdOperatorTable.CASE, countEqOne, + nul, countMinusOne); } - final RexNode result = rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, diff, denominator); - return rexBuilder.makeCast(oldCall.getType(), result); + return rexBuilder.makeCall(SqlStdOperatorTable.DIVIDE, diff, denominator); } /** diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateValuesRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateValuesRule.java index d61cb95..67e474a 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateValuesRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateValuesRule.java @@ -89,8 +89,7 @@ public class AggregateValuesRule extends RelOptRule { case MIN: case MAX: case SUM: - literals.add((RexLiteral) rexBuilder.makeCast( - aggregateCall.getType(), rexBuilder.constantNull())); + literals.add(rexBuilder.makeNullLiteral(aggregateCall.getType())); break; default: diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java b/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java index 57a108c..a8cbc93 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptOptimizeJoinRule.java @@ -1651,8 +1651,7 @@ public class LoptOptimizeJoinRule extends RelOptRule { newType = typeFactory.createTypeWithNullability(newType, true); } - projExpr = - rexBuilder.makeCast(newType, rexBuilder.constantNull()); + projExpr = rexBuilder.makeNullLiteral(newType); } else { RelDataTypeField mappedField = currFields.get(replacementKeys[i]); RexNode mappedInput = diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java index eb1ae08..39b257a 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java @@ -32,14 +32,15 @@ import org.apache.calcite.rel.metadata.RelMetadataQuery; import org.apache.calcite.rex.LogicVisitor; import org.apache.calcite.rex.RexCorrelVariable; import org.apache.calcite.rex.RexInputRef; +import org.apache.calcite.rex.RexLiteral; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexShuttle; import org.apache.calcite.rex.RexSubQuery; import org.apache.calcite.rex.RexUtil; +import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.fun.SqlQuantifyOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; -import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql2rel.RelDecorrelator; import org.apache.calcite.tools.RelBuilder; import org.apache.calcite.tools.RelBuilderFactory; @@ -167,9 +168,15 @@ public abstract class SubQueryRemoveRule extends RelOptRule { assert op == SqlStdOperatorTable.SOME_GE || op == SqlStdOperatorTable.SOME_LE || op == SqlStdOperatorTable.SOME_LT || op == SqlStdOperatorTable.SOME_GT; - RexNode caseRexNode = null; - boolean useMin = op.comparisonKind == SqlKind.GREATER_THAN - || op.comparisonKind == SqlKind.GREATER_THAN_OR_EQUAL; + final RexNode caseRexNode; + final RexNode literalFalse = builder.literal(false); + final RexNode literalTrue = builder.literal(true); + final RexLiteral literalUnknown = + builder.getRexBuilder().makeNullLiteral(literalFalse.getType()); + final SqlAggFunction minMax = op.comparisonKind == SqlKind.GREATER_THAN + || op.comparisonKind == SqlKind.GREATER_THAN_OR_EQUAL + ? SqlStdOperatorTable.MIN + : SqlStdOperatorTable.MAX; if (variablesSet.isEmpty()) { // for non-correlated case queries such as @@ -191,8 +198,7 @@ public abstract class SubQueryRemoveRule extends RelOptRule { // from emp) as q builder.push(e.rel). aggregate(builder.groupKey(), - useMin ? builder.min("m", builder.field(0)) - : builder.max("m", builder.field(0)), + builder.aggregateCall(minMax, builder.field(0)).as("m"), builder.count(false, "c"), builder.count(false, "d", builder.field(0))) .as("q") @@ -200,20 +206,21 @@ public abstract class SubQueryRemoveRule extends RelOptRule { caseRexNode = builder.call(SqlStdOperatorTable.CASE, builder.call(SqlStdOperatorTable.EQUALS, builder.field("q", "c"), builder.literal(0)), - builder.literal(false), + literalFalse, builder.call(SqlStdOperatorTable.IS_TRUE, - builder.call(RelOptUtil.op(op.comparisonKind, null), e.operands.get(0), - builder.field("q", "m"))), - builder.literal(true), - builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field("q", "c"), - builder.field("q", "d")), - e.rel.getCluster().getRexBuilder().makeNullLiteral( - builder.getTypeFactory().createSqlType(SqlTypeName.BOOLEAN)), - builder.call(RelOptUtil.op(op.comparisonKind, null), e.operands.get(0), - builder.field("q", "m"))); + builder.call(RelOptUtil.op(op.comparisonKind, null), + e.operands.get(0), builder.field("q", "m"))), + literalTrue, + builder.call(SqlStdOperatorTable.GREATER_THAN, + builder.field("q", "c"), builder.field("q", "d")), + literalUnknown, + builder.call(RelOptUtil.op(op.comparisonKind, null), + e.operands.get(0), builder.field("q", "m"))); } else { // for correlated case queries such as - // select e.deptno, e.deptno < some (select deptno from emp where emp.name = e.name) as v + // + // select e.deptno, e.deptno < some ( + // select deptno from emp where emp.name = e.name) as v // from emp as e // // becomes @@ -228,44 +235,44 @@ public abstract class SubQueryRemoveRule extends RelOptRule { // end as v // from emp as e // left outer join ( - // select max(deptno) as m, count(*) as c, count(deptno) as d, "alwaysTrue" as indicator + // select max(deptno) as m, count(*) as c, count(deptno) as d, + // "alwaysTrue" as indicator // group by name from emp) as q on e.name = q.name builder.push(e.rel) .aggregate(builder.groupKey(), - useMin ? builder.min("m", builder.field(0)) - : builder.max("m", builder.field(0)), + builder.aggregateCall(minMax, builder.field(0)).as("m"), builder.count(false, "c"), builder.count(false, "d", builder.field(0))); final List<RexNode> parentQueryFields = new ArrayList<>(); parentQueryFields.addAll(builder.fields()); String indicator = "trueLiteral"; - parentQueryFields.add(builder.alias(builder.literal(true), indicator)); + parentQueryFields.add(builder.alias(literalTrue, indicator)); builder.project(parentQueryFields).as("q"); - builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet); + builder.join(JoinRelType.LEFT, literalTrue, variablesSet); caseRexNode = builder.call(SqlStdOperatorTable.CASE, - builder.call(SqlStdOperatorTable.IS_NULL, builder.field("q", indicator)), - builder.literal(false), + builder.call(SqlStdOperatorTable.IS_NULL, + builder.field("q", indicator)), + literalFalse, builder.call(SqlStdOperatorTable.EQUALS, builder.field("q", "c"), builder.literal(0)), - builder.literal(false), + literalFalse, builder.call(SqlStdOperatorTable.IS_TRUE, - builder.call(RelOptUtil.op(op.comparisonKind, null), e.operands.get(0), - builder.field("q", "m"))), - builder.literal(true), - builder.call(SqlStdOperatorTable.GREATER_THAN, builder.field("q", "c"), - builder.field("q", "d")), - e.rel.getCluster().getRexBuilder().makeNullLiteral( - builder.getTypeFactory().createSqlType(SqlTypeName.BOOLEAN)), - builder.call(RelOptUtil.op(op.comparisonKind, null), e.operands.get(0), - builder.field("q", "m"))); + builder.call(RelOptUtil.op(op.comparisonKind, null), + e.operands.get(0), builder.field("q", "m"))), + literalTrue, + builder.call(SqlStdOperatorTable.GREATER_THAN, + builder.field("q", "c"), builder.field("q", "d")), + literalUnknown, + builder.call(RelOptUtil.op(op.comparisonKind, null), + e.operands.get(0), builder.field("q", "m"))); } // CASE statement above is created with nullable boolean type, but it might // not be correct. If the original sub-query node's type is not nullable it - // is guranteed for case statement to not produce NULLs. Therefore to avoid + // is guaranteed for case statement to not produce NULLs. Therefore to avoid // planner complaining we need to add cast. Note that nullable type is - // created due to MIN aggcall, since there is no groupby. + // created due to the MIN aggregate call, since there is no GROUP BY. if (!e.getType().isNullable()) { return builder.cast(caseRexNode, e.getType().getSqlTypeName()); } @@ -411,6 +418,10 @@ public abstract class SubQueryRemoveRule extends RelOptRule { .map(builder::isNull) .collect(Collectors.toList()); + final RexLiteral trueLiteral = (RexLiteral) builder.literal(true); + final RexLiteral falseLiteral = (RexLiteral) builder.literal(false); + final RexLiteral unknownLiteral = + builder.getRexBuilder().makeNullLiteral(trueLiteral.getType()); if (allLiterals) { final List<RexNode> conditions = Pair.zip(expressionOperands, fields).stream() @@ -420,7 +431,7 @@ public abstract class SubQueryRemoveRule extends RelOptRule { case TRUE: case TRUE_FALSE: builder.filter(conditions); - builder.project(builder.alias(builder.literal(true), "cs")); + builder.project(builder.alias(trueLiteral, "cs")); builder.distinct(); break; default: @@ -472,15 +483,15 @@ public abstract class SubQueryRemoveRule extends RelOptRule { builder.count(builder.fields()).as("ck")); builder.as("ct"); if (!variablesSet.isEmpty()) { - builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet); + builder.join(JoinRelType.LEFT, trueLiteral, variablesSet); } else { - builder.join(JoinRelType.INNER, builder.literal(true), variablesSet); + builder.join(JoinRelType.INNER, trueLiteral, variablesSet); } offset += 2; builder.push(e.rel); // fall through default: - fields.add(builder.alias(builder.literal(true), "i")); + fields.add(builder.alias(trueLiteral, "i")); builder.project(fields); builder.distinct(); } @@ -495,16 +506,16 @@ public abstract class SubQueryRemoveRule extends RelOptRule { switch (logic) { case TRUE: builder.join(JoinRelType.INNER, builder.and(conditions), variablesSet); - return builder.literal(true); + return trueLiteral; } // Now the left join builder.join(JoinRelType.LEFT, builder.and(conditions), variablesSet); final ImmutableList.Builder<RexNode> operands = ImmutableList.builder(); - Boolean b = true; + RexLiteral b = trueLiteral; switch (logic) { case TRUE_FALSE_UNKNOWN: - b = null; + b = unknownLiteral; // fall through case UNKNOWN_AS_TRUE: if (allLiterals) { @@ -513,29 +524,29 @@ public abstract class SubQueryRemoveRule extends RelOptRule { if (variablesSet.isEmpty()) { operands.add( builder.isNull(builder.field("c")), - builder.literal(false)); + falseLiteral); } operands.add( - builder.equals(builder.field("cs"), builder.literal(false)), - builder.literal(b)); + builder.equals(builder.field("cs"), falseLiteral), + b); } else { operands.add( builder.equals(builder.field("ct", "c"), builder.literal(0)), - builder.literal(false)); + falseLiteral); } break; } if (!keyIsNulls.isEmpty()) { - operands.add(builder.or(keyIsNulls), builder.literal(null)); + operands.add(builder.or(keyIsNulls), unknownLiteral); } if (allLiterals) { operands.add(builder.isNotNull(builder.field("cs")), - builder.literal(true)); + trueLiteral); } else { operands.add(builder.isNotNull(Util.last(builder.fields())), - builder.literal(true)); + trueLiteral); } if (!allLiterals) { @@ -545,10 +556,10 @@ public abstract class SubQueryRemoveRule extends RelOptRule { operands.add( builder.call(SqlStdOperatorTable.LESS_THAN, builder.field("ct", "ck"), builder.field("ct", "c")), - builder.literal(b)); + b); } } - operands.add(builder.literal(false)); + operands.add(falseLiteral); return builder.call(SqlStdOperatorTable.CASE, operands.build()); } diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java index 3774430..3434079 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ValuesReduceRule.java @@ -196,9 +196,9 @@ public abstract class ValuesReduceRule extends RelOptRule { ImmutableList.builder(); for (int row = 0; row < values.getTuples().size(); ++row) { int i = 0; - RexNode reducedValue; if (conditionExpr != null) { - reducedValue = reducibleExps.get((row * fieldsPerRow) + i); + final RexNode reducedValue = + reducibleExps.get((row * fieldsPerRow) + i); ++i; if (!reducedValue.isAlwaysTrue()) { ++changeCount; @@ -206,17 +206,18 @@ public abstract class ValuesReduceRule extends RelOptRule { } } - ImmutableList<RexLiteral> valuesList; + final ImmutableList<RexLiteral> valuesList; if (projectExprs != null) { ++changeCount; final ImmutableList.Builder<RexLiteral> tupleBuilder = ImmutableList.builder(); for (; i < fieldsPerRow; ++i) { - reducedValue = reducibleExps.get((row * fieldsPerRow) + i); + final RexNode reducedValue = + reducibleExps.get((row * fieldsPerRow) + i); if (reducedValue instanceof RexLiteral) { tupleBuilder.add((RexLiteral) reducedValue); } else if (RexUtil.isNullLiteral(reducedValue, true)) { - tupleBuilder.add(rexBuilder.constantNull()); + tupleBuilder.add(rexBuilder.makeNullLiteral(reducedValue.getType())); } else { return; } diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java index cdb1008..3a8c650 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java +++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java @@ -407,7 +407,7 @@ public class RexBuilder { new RexOver(typeFactory.createTypeWithNullability(type, false), operator, exprs, window, distinct, ignoreNulls), false), - makeCast(type, constantNull())); + makeNullLiteral(type)); } if (!allowPartial) { Preconditions.checkArgument(physical, "DISALLOW PARTIAL over RANGE"); @@ -463,7 +463,11 @@ public class RexBuilder { /** * Creates a constant for the SQL <code>NULL</code> value. + * + * @deprecated Use {@link #makeNullLiteral(RelDataType)}, which produces a + * NULL of the correct type */ + @Deprecated // to be removed before 2.0 public RexLiteral constantNull() { return constantNull; } @@ -1252,7 +1256,7 @@ public class RexBuilder { if (!type.isNullable()) { type = typeFactory.createTypeWithNullability(type, true); } - return (RexLiteral) makeCast(type, constantNull()); + return (RexLiteral) makeCast(type, constantNull); } /** @deprecated Use {@link #makeNullLiteral(RelDataType)} */ diff --git a/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java b/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java index 1959790..bc3064c 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/NullInitializerExpressionFactory.java @@ -54,13 +54,17 @@ public class NullInitializerExpressionFactory implements InitializerExpressionFa public RexNode newColumnDefaultValue(RelOptTable table, int iColumn, InitializerContext context) { - return context.getRexBuilder().constantNull(); + final RelDataType fieldType = + table.getRowType().getFieldList().get(iColumn).getType(); + return context.getRexBuilder().makeNullLiteral(fieldType); } public RexNode newAttributeInitializer(RelDataType type, SqlFunction constructor, int iAttribute, List<RexNode> constructorArgs, InitializerContext context) { - return context.getRexBuilder().constantNull(); + final RelDataType fieldType = + type.getFieldList().get(iAttribute).getType(); + return context.getRexBuilder().makeNullLiteral(fieldType); } } diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java index cac5ca7..4facf48 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java @@ -1626,7 +1626,7 @@ public class RelDecorrelator implements ReflectiveVisitor { // nulls on the LHS, the projection now need to make a nullable LHS // reference using a nullability indicator. If this this indicator // is null, it means the sub-query does not produce any value. As a - // result, any RHS ref by this usbquery needs to produce null value. + // result, any RHS ref by this sub-query needs to produce null value. // WHEN indicator IS NULL caseOperands[0] = @@ -1640,11 +1640,9 @@ public class RelDecorrelator implements ReflectiveVisitor { // THEN CAST(NULL AS newInputTypeNullable) caseOperands[1] = - rexBuilder.makeCast( - typeFactory.createTypeWithNullability( - rexNode.getType(), - true), - lit); + lit == null + ? rexBuilder.makeNullLiteral(rexNode.getType()) + : rexBuilder.makeCast(rexNode.getType(), lit); // ELSE cast (newInput AS newInputTypeNullable) END caseOperands[2] = @@ -1674,10 +1672,7 @@ public class RelDecorrelator implements ReflectiveVisitor { // need to enforce nullability by applying an additional // cast operator over the transformed expression. newRexNode = - createCaseExpression( - nullIndicator, - rexBuilder.constantNull(), - newRexNode); + createCaseExpression(nullIndicator, null, newRexNode); } return newRexNode; } @@ -1723,10 +1718,7 @@ public class RelDecorrelator implements ReflectiveVisitor { if (!RexUtil.isNull(literal) && projectPulledAboveLeftCorrelator && (nullIndicator != null)) { - return createCaseExpression( - nullIndicator, - rexBuilder.constantNull(), - literal); + return createCaseExpression(nullIndicator, null, literal); } return literal; } @@ -1779,10 +1771,7 @@ public class RelDecorrelator implements ReflectiveVisitor { } if (projectPulledAboveLeftCorrelator && (nullIndicator != null)) { - return createCaseExpression( - nullIndicator, - rexBuilder.constantNull(), - newCall); + return createCaseExpression(nullIndicator, null, newCall); } return newCall; } diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java index fdf1762..ea84e82 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java @@ -266,10 +266,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor { nullIndicator); // THEN CAST(NULL AS StructuredType) - caseOperands[1] = - rexBuilder.makeCast( - structuredType, - rexBuilder.constantNull()); + caseOperands[1] = rexBuilder.makeNullLiteral(structuredType); // ELSE NEW StructuredType(inputs...) END caseOperands[2] = newInvocation; @@ -688,7 +685,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor { for (RelDataTypeField field : flattenedType.getFieldList()) { flattenedExps.add( Pair.of( - rexBuilder.makeCast(field.getType(), rexBuilder.constantNull()), + rexBuilder.makeNullLiteral(field.getType()), field.getName())); } } diff --git a/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java b/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java index 9f398d6..ea2eaf1 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlNodeToRexConverterImpl.java @@ -92,9 +92,7 @@ public class SqlNodeToRexConverterImpl implements SqlNodeToRexConverter { } else { type = validator.getValidatedNodeType(literal); } - return rexBuilder.makeCast( - type, - rexBuilder.constantNull()); + return rexBuilder.makeNullLiteral(type); } BitString bitString; diff --git a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java index c5b0b7d..23c52b9 100644 --- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java +++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java @@ -368,20 +368,25 @@ public class StandardConvertletTable extends ReflectiveConvertletTable { RexBuilder rexBuilder = cx.getRexBuilder(); final List<RexNode> exprList = new ArrayList<>(); + final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory(); + final RexLiteral unknownLiteral = rexBuilder.makeNullLiteral( + typeFactory.createSqlType(SqlTypeName.BOOLEAN)); + final RexLiteral nullLiteral = rexBuilder.makeNullLiteral( + typeFactory.createSqlType(SqlTypeName.NULL)); for (int i = 0; i < whenList.size(); i++) { if (SqlUtil.isNullLiteral(whenList.get(i), false)) { - exprList.add(rexBuilder.constantNull()); + exprList.add(unknownLiteral); } else { exprList.add(cx.convertExpression(whenList.get(i))); } if (SqlUtil.isNullLiteral(thenList.get(i), false)) { - exprList.add(rexBuilder.constantNull()); + exprList.add(nullLiteral); } else { exprList.add(cx.convertExpression(thenList.get(i))); } } if (SqlUtil.isNullLiteral(call.getElseOperand(), false)) { - exprList.add(rexBuilder.constantNull()); + exprList.add(nullLiteral); } else { exprList.add(cx.convertExpression(call.getElseOperand())); } diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index 0ff0c75..239d58a 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -360,7 +360,8 @@ public class RelBuilder { public RexNode literal(Object value) { final RexBuilder rexBuilder = cluster.getRexBuilder(); if (value == null) { - return rexBuilder.constantNull(); + final RelDataType type = getTypeFactory().createSqlType(SqlTypeName.NULL); + return rexBuilder.makeNullLiteral(type); } else if (value instanceof Boolean) { return rexBuilder.makeLiteral((Boolean) value); } else if (value instanceof BigDecimal) { diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java index 2be216d..736cfbd 100644 --- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java @@ -2729,11 +2729,9 @@ public class RexProgramTest extends RexProgramBuilderBase { @Test public void testIsNullSimplificationWithIsDistinctFrom() { RexNode expr = isNotNull( - case_( - vBool(), + case_(vBool(), isDistinctFrom(falseLiteral, vBoolNotNull(0)), - vBoolNotNull(2)) - ); + vBoolNotNull(2))); RexNode s = simplify.simplifyUnknownAs(expr, RexUnknownAs.UNKNOWN); assertThat(expr.isAlwaysTrue(), is(true)); diff --git a/core/src/test/java/org/apache/calcite/test/catalog/EmpInitializerExpressionFactory.java b/core/src/test/java/org/apache/calcite/test/catalog/EmpInitializerExpressionFactory.java index 4d26cba..7e5048b 100644 --- a/core/src/test/java/org/apache/calcite/test/catalog/EmpInitializerExpressionFactory.java +++ b/core/src/test/java/org/apache/calcite/test/catalog/EmpInitializerExpressionFactory.java @@ -56,7 +56,7 @@ class EmpInitializerExpressionFactory return rexBuilder.makeExactLiteral(new BigDecimal(555), typeFactory.createSqlType(SqlTypeName.INTEGER)); default: - return rexBuilder.constantNull(); + return super.newColumnDefaultValue(table, iColumn, context); } } } diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml index cb07e35..4142664 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -6683,8 +6683,7 @@ LogicalProject(NAME=[$1]) <![CDATA[select * from dept a left join (select name, 1 from dept) as b -on a.name = b.name -]]> +on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6710,8 +6709,7 @@ LogicalProject(DEPTNO=[$0], NAME=[$1], NAME0=[$2], EXPR$1=[$3]) <![CDATA[select * from (select name, 1 from dept) as a right join dept b -on a.name = b.name -]]> +on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6735,10 +6733,10 @@ LogicalProject(NAME=[$0], EXPR$1=[$1], DEPTNO=[$2], NAME0=[$3]) <TestCase name="testJoinProjectTranspose4"> <Resource name="sql"> <![CDATA[select * +from dept a left join (select x name, y is not null from (values (2, cast(null as integer)), (2, 1)) as t(x, y)) b -on a.name = b.name -]]> +on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6770,10 +6768,9 @@ LogicalProject(DEPTNO=[$0], NAME=[$1], NAME0=[$2], EXPR$1=[$3]) <TestCase name="testJoinProjectTranspose5"> <Resource name="sql"> <![CDATA[select * -left join (select x name, y is not null from -(values (2, cast(null as integer)), (2, 1)) as t(x, y)) b -on a.name = b.name -]]> +from dept a +left join (select name, 1 + 1 from dept) as b +on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6799,8 +6796,7 @@ LogicalProject(DEPTNO=[$0], NAME=[$1], NAME0=[$2], EXPR$1=[$3]) <![CDATA[select * from (select name, 1 from dept) a full join (select name, 1 from dept) as b -on a.name = b.name -]]> +on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6828,8 +6824,7 @@ LogicalProject(NAME=[$0], EXPR$1=[$1], NAME0=[$2], EXPR$10=[$3]) <![CDATA[select * from dept a left join (select name from dept) as b -on a.name = b.name -]]> + on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -6854,9 +6849,10 @@ LogicalProject(DEPTNO=[$0], NAME=[$1], NAME0=[$2]) <Resource name="sql"> <![CDATA[select * from dept a -left join (select name, deptno > 10 and cast(null as boolean) from dept b) as b -on a.name = b.name -]]> +left join ( + select name, deptno > 10 and cast(null as boolean) + from dept) as b +on a.name = b.name]]> </Resource> <Resource name="planBefore"> <![CDATA[ @@ -10146,7 +10142,7 @@ LogicalProject(DEPTNO=[$1]) </Resource> <Resource name="planAfter"> <![CDATA[ -LogicalProject(EMPNO=[$0], D=[CASE(=($9, 0), false, IS NULL(CASE(true, CAST($7):INTEGER, null:INTEGER)), null:NULL, IS NOT NULL($12), true, <($10, $9), null:NULL, false)]) +LogicalProject(EMPNO=[$0], D=[CASE(=($9, 0), false, IS NULL(CASE(true, CAST($7):INTEGER, null:INTEGER)), null:BOOLEAN, IS NOT NULL($12), true, <($10, $9), null:BOOLEAN, false)]) LogicalJoin(condition=[=(CASE(true, CAST($7):INTEGER, null:INTEGER), $11)], joinType=[left]) LogicalJoin(condition=[true], joinType=[inner]) LogicalTableScan(table=[[CATALOG, SALES, EMP]]) diff --git a/core/src/test/resources/sql/blank.iq b/core/src/test/resources/sql/blank.iq index 93484e5..763d58b 100644 --- a/core/src/test/resources/sql/blank.iq +++ b/core/src/test/resources/sql/blank.iq @@ -73,7 +73,7 @@ insert into table2 values (NULL, 1), (2, 1); # Checked on Oracle !set lateDecorrelate true select i, j from table1 where table1.j NOT IN (select i from table2 where table1.i=table2.j); -EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t3, $t8)], expr#10=[IS NOT TRUE($t9)], expr#11=[IS NOT NULL($t7)], expr#12=[<($t4, $t3)], expr#13=[OR($t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[IS NOT NULL($t1)], expr#16=[AND($t10, $t14, $t15)], expr#17=[OR($t9, $t16)], proj#0..1=[{exprs}], $condition=[$t17]) +EnumerableCalc(expr#0..7=[{inputs}], expr#8=[0], expr#9=[=($t3, $t8)], expr#10=[IS NULL($t1)], expr#11=[IS NOT NULL($t7)], expr#12=[<($t4, $t3)], expr#13=[OR($t10, $t11, $t12)], expr#14=[IS NOT TRUE($t13)], expr#15=[OR($t9, $t14)], proj#0..1=[{exprs}], $condition=[$t15]) EnumerableHashJoin(condition=[AND(=($0, $6), =($1, $5))], joinType=[left]) EnumerableHashJoin(condition=[=($0, $2)], joinType=[left]) EnumerableTableScan(table=[[BLANK, TABLE1]]) diff --git a/core/src/test/resources/sql/sub-query.iq b/core/src/test/resources/sql/sub-query.iq index 3629fb5..595ca2b 100644 --- a/core/src/test/resources/sql/sub-query.iq +++ b/core/src/test/resources/sql/sub-query.iq @@ -32,7 +32,7 @@ where t1.x not in (select t2.x from t2); (0 rows) !ok -EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0], expr#6=[=($t1, $t5)], expr#7=[<>($t1, $t5)], expr#8=[IS NULL($t4)], expr#9=[>=($t2, $t1)], expr#10=[IS NOT NULL($t0)], expr#11=[AND($t7, $t8, $t9, $t10)], expr#12=[OR($t6, $t11)], X=[$t0], $condition=[$t12]) +EnumerableCalc(expr#0..4=[{inputs}], expr#5=[0], expr#6=[=($t1, $t5)], expr#7=[IS NULL($t4)], expr#8=[>=($t2, $t1)], expr#9=[IS NOT NULL($t0)], expr#10=[AND($t7, $t8, $t9)], expr#11=[OR($t6, $t10)], X=[$t0], $condition=[$t11]) EnumerableHashJoin(condition=[=($0, $3)], joinType=[left]) EnumerableHashJoin(condition=[true], joinType=[inner]) EnumerableUnion(all=[true]) @@ -2047,7 +2047,7 @@ where sal + 100 not in ( !ok EnumerableAggregate(group=[{}], C=[COUNT()]) - EnumerableCalc(expr#0..9=[{inputs}], expr#10=[0], expr#11=[=($t4, $t10)], expr#12=[IS NOT TRUE($t11)], expr#13=[IS NOT NULL($t7)], expr#14=[<($t5, $t4)], expr#15=[OR($t13, $t14)], expr#16=[IS NOT TRUE($t15)], expr#17=[IS NOT NULL($t2)], expr#18=[AND($t12, $t16, $t17)], expr#19=[OR($t11, $t18)], proj#0..9=[{exprs}], $condition=[$t19]) + EnumerableCalc(expr#0..9=[{inputs}], expr#10=[0], expr#11=[=($t4, $t10)], expr#12=[IS NULL($t2)], expr#13=[IS NOT NULL($t7)], expr#14=[<($t5, $t4)], expr#15=[OR($t12, $t13, $t14)], expr#16=[IS NOT TRUE($t15)], expr#17=[OR($t11, $t16)], proj#0..9=[{exprs}], $condition=[$t17]) EnumerableHashJoin(condition=[AND(=($1, $8), =($2, $9))], joinType=[left]) EnumerableHashJoin(condition=[=($1, $3)], joinType=[left]) EnumerableCalc(expr#0..7=[{inputs}], proj#0..1=[{exprs}], SAL=[$t5])
