You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2022/11/22 06:06:04 UTC
[calcite] branch main updated: [CALCITE-5336] Infer constants from predicates with IS NOT DISTINCT FROM operator
This is an automated email from the ASF dual-hosted git repository.
jhyde pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 5a9cd9f259 [CALCITE-5336] Infer constants from predicates with IS NOT DISTINCT FROM operator
5a9cd9f259 is described below
commit 5a9cd9f259c4dcc34a1bc1291dfcd188b3128156
Author: jtrada168 <ja...@dremio.com>
AuthorDate: Wed Nov 2 18:30:43 2022 -0700
[CALCITE-5336] Infer constants from predicates with IS NOT DISTINCT FROM operator
Support DateString in RelBuilder.literal(Object).
Close apache/calcite#2961
---
.../apache/calcite/prepare/CalcitePrepareImpl.java | 3 ++
.../main/java/org/apache/calcite/rex/RexUtil.java | 25 ++++++++------
.../java/org/apache/calcite/tools/RelBuilder.java | 3 ++
.../org/apache/calcite/rex/RexProgramTest.java | 27 ++++++++++++++-
.../org/apache/calcite/test/RelOptRulesTest.java | 40 ++++++++++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 16 +++++++++
6 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 6d06705677..ad0fff75f5 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.prepare;
+import org.apache.calcite.DataContexts;
import org.apache.calcite.adapter.enumerable.EnumerableCalc;
import org.apache.calcite.adapter.enumerable.EnumerableConvention;
import org.apache.calcite.adapter.enumerable.EnumerableInterpretable;
@@ -72,6 +73,7 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexExecutorImpl;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexProgram;
@@ -442,6 +444,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
}
final VolcanoPlanner planner =
new VolcanoPlanner(costFactory, externalContext);
+ planner.setExecutor(new RexExecutorImpl(DataContexts.EMPTY));
planner.addRelTraitDef(ConventionTraitDef.INSTANCE);
if (CalciteSystemProperty.ENABLE_COLLATION_TRAIT.value()) {
planner.addRelTraitDef(RelCollationTraitDef.INSTANCE);
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index b68eb43831..9fb321a905 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -400,24 +400,27 @@ public class RexUtil {
private static <C extends RexNode> void gatherConstraints(Class<C> clazz,
RexNode predicate, Map<RexNode, C> map, Set<RexNode> excludeSet,
RexBuilder rexBuilder) {
- if (predicate.getKind() != SqlKind.EQUALS
- && predicate.getKind() != SqlKind.IS_NULL) {
- decompose(excludeSet, predicate);
- return;
- }
- final List<RexNode> operands = ((RexCall) predicate).getOperands();
final RexNode left;
final RexNode right;
- if (predicate.getKind() == SqlKind.EQUALS) {
- left = operands.get(0);
- right = operands.get(1);
- } else { // is null
- left = operands.get(0);
+ switch (predicate.getKind()) {
+ case EQUALS:
+ case IS_NOT_DISTINCT_FROM:
+ left = ((RexCall) predicate).getOperands().get(0);
+ right = ((RexCall) predicate).getOperands().get(1);
+ break;
+
+ case IS_NULL:
+ left = ((RexCall) predicate).getOperands().get(0);
if (!left.getType().isNullable()) {
// There's no sense in inferring $0=null when $0 is not nullable
return;
}
right = rexBuilder.makeNullLiteral(left.getType());
+ break;
+
+ default:
+ decompose(excludeSet, predicate);
+ return;
}
// Note that literals are immutable too, and they can only be compared
// through values.
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 fdf6f0ab62..f48b14272c 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -99,6 +99,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.TableFunctionReturnTypeInference;
import org.apache.calcite.sql.validate.SqlValidatorUtil;
import org.apache.calcite.sql2rel.SqlToRelConverter;
+import org.apache.calcite.util.DateString;
import org.apache.calcite.util.Holder;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.ImmutableIntList;
@@ -475,6 +476,8 @@ public class RelBuilder {
} else if (value instanceof Enum) {
return rexBuilder.makeLiteral(value,
getTypeFactory().createSqlType(SqlTypeName.SYMBOL));
+ } else if (value instanceof DateString) {
+ return rexBuilder.makeDateLiteral((DateString) value);
} else {
throw new IllegalArgumentException("cannot convert " + value
+ " (" + value.getClass() + ") to a constant");
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 637f83a48b..194869993e 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
@@ -2779,6 +2779,30 @@ class RexProgramTest extends RexProgramTestBase {
assertThat(getString(map7), is("{1=CAST(?0.a):BIGINT NOT NULL, ?0.a=1}"));
}
+ /** Unit test for {@link RexUtil#predicateConstants(Class, RexBuilder, List)}
+ * applied to a predicate with {@code IS NOT DISTINCT FROM}. */
+ @Test void testConstantMapIsNotDistinctFrom() {
+ final RelDataType dateColumnType =
+ typeFactory.createTypeWithNullability(
+ typeFactory.createSqlType(SqlTypeName.DATE), true);
+
+ final RexNode dateLiteral =
+ rexBuilder.makeLiteral(new DateString(2020, 12, 11), dateColumnType,
+ false);
+ final RexNode dateColumn = rexBuilder.makeInputRef(dateColumnType, 0);
+
+ final RexNode call =
+ rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM,
+ dateColumn, dateLiteral);
+ final Map<RexNode, RexNode> map =
+ RexUtil.predicateConstants(RexNode.class, rexBuilder,
+ ImmutableList.of(call));
+
+ assertThat(map.isEmpty(), is(false));
+ assertThat(dateLiteral, is(map.get(dateColumn)));
+ assertThat(getString(map), is("{$0=2020-12-11}"));
+ }
+
@Test void notDistinct() {
checkSimplify(
isFalse(isNotDistinctFrom(vBool(0), vBool(1))),
@@ -2850,7 +2874,7 @@ class RexProgramTest extends RexProgramTestBase {
/** Converts a map to a string, sorting on the string representation of its
* keys. */
- private static String getString(ImmutableMap<RexNode, RexNode> map) {
+ private static String getString(Map<RexNode, RexNode> map) {
final TreeMap<String, RexNode> map2 = new TreeMap<>();
for (Map.Entry<RexNode, RexNode> entry : map.entrySet()) {
map2.put(entry.getKey().toString(), entry.getValue());
@@ -3404,4 +3428,5 @@ class RexProgramTest extends RexProgramTestBase {
checkSimplify(add(zero, sub(nullInt, nullInt)), "null:INTEGER");
}
+
}
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index a812b27bc5..a1262be913 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -121,6 +121,7 @@ import org.apache.calcite.tools.Programs;
import org.apache.calcite.tools.RelBuilder;
import org.apache.calcite.tools.RuleSet;
import org.apache.calcite.tools.RuleSets;
+import org.apache.calcite.util.DateString;
import org.apache.calcite.util.ImmutableBitSet;
import com.google.common.collect.ImmutableList;
@@ -4332,6 +4333,13 @@ class RelOptRulesTest extends RelOptTestBase {
.check();
}
+ /** Tests propagation of a filter derived from an "IS NOT DISTINCT FROM"
+ * predicate.
+ *
+ * <p>By the time the rule sees the predicate, it has been converted to
+ * "deptno = 10 OR (deptno IS NULL AND 10 IS NULL)", so
+ * {@link #testPullConstantIntoProjectWithIsNotDistinctFromDate()} is a purer
+ * test of the functionality. */
@Test void testPullConstantIntoProjectWithIsNotDistinctFrom() {
final String sql = "select deptno, deptno + 1, empno + deptno\n"
+ "from sales.emp\n"
@@ -4352,6 +4360,37 @@ class RelOptRulesTest extends RelOptTestBase {
.check();
}
+ /** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-5336">[CALCITE-5336]
+ * Infer constants from predicates with IS NOT DISTINCT FROM operator</a>.
+ *
+ * <p>Reduce expression rules should identify constants with
+ * {@code IS NOT DISTINCT FROM} operator as they do for {@code =}. */
+ @Test void testPullConstantIntoProjectWithIsNotDistinctFromDate() {
+ // Build a tree equivalent to the SQL
+ // SELECT hiredate
+ // FROM emp
+ // WHERE hiredate IS NOT DISTINCT FROM DATE '2020-12-11'
+ //
+ // We build a tree because if we used the SQL parser, it would expand
+ // x IS NOT DISTINCT FROM y
+ // to
+ // x = y OR (x IS NULL AND y IS NULL)
+ final Function<RelBuilder, RelNode> relFn = b ->
+ b.scan("EMP")
+ .filter(
+ b.getRexBuilder()
+ .makeCall(SqlStdOperatorTable.IS_NOT_DISTINCT_FROM,
+ b.field(4),
+ b.literal(new DateString(2020, 12, 11))))
+ .project(b.field(4))
+ .build();
+
+ relFn(relFn)
+ .withRule(CoreRules.PROJECT_REDUCE_EXPRESSIONS)
+ .check();
+ }
+
@Test void testPullConstantIntoFilter() {
final String sql = "select * from (select * from sales.emp where deptno = 10)\n"
+ "where deptno + 5 > empno";
@@ -7448,4 +7487,5 @@ class RelOptRulesTest extends RelOptTestBase {
.withRule(CoreRules.AGGREGATE_EXPAND_DISTINCT_AGGREGATES_TO_JOIN)
.check();
}
+
}
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 bdcb9595e1..27010f6367 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -7259,6 +7259,22 @@ LogicalProject(DEPTNO=[$7], EXPR$1=[+($7, 1)], EXPR$2=[+($0, $7)])
LogicalProject(DEPTNO=[10], EXPR$1=[11], EXPR$2=[+($0, 10)])
LogicalFilter(condition=[OR(AND(IS NULL($7), IS NULL(10)), IS TRUE(=($7, 10)))])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testPullConstantIntoProjectWithIsNotDistinctFromDate">
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalProject(HIREDATE=[$4])
+ LogicalFilter(condition=[IS NOT DISTINCT FROM($4, 2020-12-11)])
+ LogicalTableScan(table=[[scott, EMP]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalProject(HIREDATE=[CAST(2020-12-11):DATE])
+ LogicalFilter(condition=[IS NOT DISTINCT FROM($4, 2020-12-11)])
+ LogicalTableScan(table=[[scott, EMP]])
]]>
</Resource>
</TestCase>