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 2016/10/24 22:01:27 UTC
[2/2] calcite git commit: [CALCITE-1465] Store constants as a derived
field in RelOptPredicateList
[CALCITE-1465] Store constants as a derived field in RelOptPredicateList
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/0f36aec7
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/0f36aec7
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/0f36aec7
Branch: refs/heads/master
Commit: 0f36aec7fd2c8de1acfe381af7bb4f0690511377
Parents: 2ee0e35
Author: Julian Hyde <jh...@apache.org>
Authored: Mon Oct 24 00:25:29 2016 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Mon Oct 24 15:01:13 2016 -0700
----------------------------------------------------------------------
.../calcite/plan/RelOptPredicateList.java | 50 ++++--
.../calcite/rel/metadata/RelMdPredicates.java | 22 ++-
.../AggregateProjectPullUpConstantsRule.java | 9 +-
.../rel/rules/ReduceExpressionsRule.java | 167 ++-----------------
.../rel/rules/UnionPullUpConstantsRule.java | 25 ++-
.../java/org/apache/calcite/rex/RexUtil.java | 159 ++++++++++++++++++
.../org/apache/calcite/test/RexProgramTest.java | 61 +++++++
7 files changed, 293 insertions(+), 200 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
index 1dce56d..1acc86f 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptPredicateList.java
@@ -16,10 +16,13 @@
*/
package org.apache.calcite.plan;
+import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
/**
* Predicates that are known to hold in the output of a particular relational
@@ -63,7 +66,8 @@ import com.google.common.collect.ImmutableList;
public class RelOptPredicateList {
private static final ImmutableList<RexNode> EMPTY_LIST = ImmutableList.of();
public static final RelOptPredicateList EMPTY =
- new RelOptPredicateList(EMPTY_LIST, EMPTY_LIST, EMPTY_LIST);
+ new RelOptPredicateList(EMPTY_LIST, EMPTY_LIST, EMPTY_LIST,
+ ImmutableMap.<RexNode, RexNode>of());
/** Predicates that can be pulled up from the relational expression and its
* inputs. */
@@ -77,13 +81,20 @@ public class RelOptPredicateList {
* Empty if the relational expression is not a join. */
public final ImmutableList<RexNode> rightInferredPredicates;
- private RelOptPredicateList(Iterable<RexNode> pulledUpPredicates,
- Iterable<RexNode> leftInferredPredicates,
- Iterable<RexNode> rightInferredPredicates) {
- this.pulledUpPredicates = ImmutableList.copyOf(pulledUpPredicates);
- this.leftInferredPredicates = ImmutableList.copyOf(leftInferredPredicates);
+ /** A map of each (e, constant) pair that occurs within
+ * {@link #pulledUpPredicates}. */
+ public final ImmutableMap<RexNode, RexNode> constantMap;
+
+ private RelOptPredicateList(ImmutableList<RexNode> pulledUpPredicates,
+ ImmutableList<RexNode> leftInferredPredicates,
+ ImmutableList<RexNode> rightInferredPredicates,
+ ImmutableMap<RexNode, RexNode> constantMap) {
+ this.pulledUpPredicates = Preconditions.checkNotNull(pulledUpPredicates);
+ this.leftInferredPredicates =
+ Preconditions.checkNotNull(leftInferredPredicates);
this.rightInferredPredicates =
- ImmutableList.copyOf(rightInferredPredicates);
+ Preconditions.checkNotNull(rightInferredPredicates);
+ this.constantMap = Preconditions.checkNotNull(constantMap);
}
/** Creates a RelOptPredicateList with only pulled-up predicates, no inferred
@@ -94,18 +105,19 @@ public class RelOptPredicateList {
* @param pulledUpPredicates Predicates that apply to the rows returned by the
* relational expression
*/
- public static RelOptPredicateList of(Iterable<RexNode> pulledUpPredicates) {
+ public static RelOptPredicateList of(RexBuilder rexBuilder,
+ Iterable<RexNode> pulledUpPredicates) {
ImmutableList<RexNode> pulledUpPredicatesList =
ImmutableList.copyOf(pulledUpPredicates);
if (pulledUpPredicatesList.isEmpty()) {
return EMPTY;
}
- return new RelOptPredicateList(pulledUpPredicatesList, EMPTY_LIST,
- EMPTY_LIST);
+ return of(rexBuilder, pulledUpPredicatesList, EMPTY_LIST, EMPTY_LIST);
}
/** Creates a RelOptPredicateList for a join.
*
+ * @param rexBuilder Rex builder
* @param pulledUpPredicates Predicates that apply to the rows returned by the
* relational expression
* @param leftInferredPredicates Predicates that were inferred from the right
@@ -113,7 +125,8 @@ public class RelOptPredicateList {
* @param rightInferredPredicates Predicates that were inferred from the left
* input
*/
- public static RelOptPredicateList of(Iterable<RexNode> pulledUpPredicates,
+ public static RelOptPredicateList of(RexBuilder rexBuilder,
+ Iterable<RexNode> pulledUpPredicates,
Iterable<RexNode> leftInferredPredicates,
Iterable<RexNode> rightInferredPredicates) {
final ImmutableList<RexNode> pulledUpPredicatesList =
@@ -127,12 +140,16 @@ public class RelOptPredicateList {
&& rightInferredPredicatesList.isEmpty()) {
return EMPTY;
}
+ final ImmutableMap<RexNode, RexNode> constantMap =
+ RexUtil.predicateConstants(RexNode.class, rexBuilder,
+ pulledUpPredicatesList);
return new RelOptPredicateList(pulledUpPredicatesList,
- leftInferredPredicateList, rightInferredPredicatesList);
+ leftInferredPredicateList, rightInferredPredicatesList, constantMap);
}
- public RelOptPredicateList union(RelOptPredicateList list) {
- return RelOptPredicateList.of(
+ public RelOptPredicateList union(RexBuilder rexBuilder,
+ RelOptPredicateList list) {
+ return RelOptPredicateList.of(rexBuilder,
concat(pulledUpPredicates, list.pulledUpPredicates),
concat(leftInferredPredicates, list.leftInferredPredicates),
concat(rightInferredPredicates, list.rightInferredPredicates));
@@ -150,8 +167,9 @@ public class RelOptPredicateList {
}
}
- public RelOptPredicateList shift(int offset) {
- return RelOptPredicateList.of(RexUtil.shift(pulledUpPredicates, offset),
+ public RelOptPredicateList shift(RexBuilder rexBuilder, int offset) {
+ return RelOptPredicateList.of(rexBuilder,
+ RexUtil.shift(pulledUpPredicates, offset),
RexUtil.shift(leftInferredPredicates, offset),
RexUtil.shift(rightInferredPredicates, offset));
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
index e8f3790..ba0d6d6 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java
@@ -217,7 +217,7 @@ public class RelMdPredicates
projectPullUpPredicates.add(rexBuilder.makeCall(op, args));
}
}
- return RelOptPredicateList.of(projectPullUpPredicates);
+ return RelOptPredicateList.of(rexBuilder, projectPullUpPredicates);
}
/**
@@ -225,11 +225,12 @@ public class RelMdPredicates
*/
public RelOptPredicateList getPredicates(Filter filter, RelMetadataQuery mq) {
final RelNode input = filter.getInput();
+ final RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
return Util.first(inputInfo, RelOptPredicateList.EMPTY)
- .union(
- RelOptPredicateList.of(
+ .union(filter.getCluster().getRexBuilder(),
+ RelOptPredicateList.of(rexBuilder,
RelOptUtil.conjunctions(filter.getCondition())));
}
@@ -283,6 +284,7 @@ public class RelMdPredicates
*/
public RelOptPredicateList getPredicates(Aggregate agg, RelMetadataQuery mq) {
final RelNode input = agg.getInput();
+ final RexBuilder rexBuilder = agg.getCluster().getRexBuilder();
final RelOptPredicateList inputInfo = mq.getPulledUpPredicates(input);
final List<RexNode> aggPullUpPredicates = new ArrayList<>();
@@ -309,7 +311,7 @@ public class RelMdPredicates
aggPullUpPredicates.add(r);
}
}
- return RelOptPredicateList.of(aggPullUpPredicates);
+ return RelOptPredicateList.of(rexBuilder, aggPullUpPredicates);
}
/**
@@ -362,7 +364,7 @@ public class RelMdPredicates
if (!disjPred.isAlwaysTrue()) {
preds.add(disjPred);
}
- return RelOptPredicateList.of(preds);
+ return RelOptPredicateList.of(rB, preds);
}
/**
@@ -388,11 +390,12 @@ public class RelMdPredicates
if (!Bug.CALCITE_1048_FIXED) {
return RelOptPredicateList.EMPTY;
}
+ final RexBuilder rexBuilder = r.getCluster().getRexBuilder();
RelOptPredicateList list = null;
for (RelNode r2 : r.getRels()) {
RelOptPredicateList list2 = mq.getPulledUpPredicates(r2);
if (list2 != null) {
- list = list == null ? list2 : list.union(list2);
+ list = list == null ? list2 : list.union(rexBuilder, list2);
}
}
return Util.first(list, RelOptPredicateList.EMPTY);
@@ -572,6 +575,7 @@ public class RelMdPredicates
}
}
+ final RexBuilder rexBuilder = joinRel.getCluster().getRexBuilder();
switch (joinType) {
case INNER:
Iterable<RexNode> pulledUpPredicates;
@@ -586,14 +590,14 @@ public class RelMdPredicates
RelOptUtil.conjunctions(joinRel.getCondition()),
inferredPredicates);
}
- return RelOptPredicateList.of(pulledUpPredicates,
+ return RelOptPredicateList.of(rexBuilder, pulledUpPredicates,
leftInferredPredicates, rightInferredPredicates);
case LEFT:
- return RelOptPredicateList.of(
+ return RelOptPredicateList.of(rexBuilder,
RelOptUtil.conjunctions(leftChildPredicates),
leftInferredPredicates, rightInferredPredicates);
case RIGHT:
- return RelOptPredicateList.of(
+ return RelOptPredicateList.of(rexBuilder,
RelOptUtil.conjunctions(rightChildPredicates),
inferredPredicates, EMPTY_LIST);
default:
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
index 3d05e89..f39cdf9 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
@@ -35,8 +35,6 @@ import org.apache.calcite.tools.RelBuilderFactory;
import org.apache.calcite.util.ImmutableBitSet;
import org.apache.calcite.util.Pair;
-import com.google.common.collect.ImmutableMap;
-
import java.util.ArrayList;
import java.util.List;
import java.util.NavigableMap;
@@ -114,15 +112,12 @@ public class AggregateProjectPullUpConstantsRule extends RelOptRule {
if (predicates == null) {
return;
}
- final ImmutableMap<RexNode, RexNode> constants =
- ReduceExpressionsRule.predicateConstants(RexNode.class, rexBuilder,
- predicates);
final NavigableMap<Integer, RexNode> map = new TreeMap<>();
for (int key : aggregate.getGroupSet()) {
final RexInputRef ref =
rexBuilder.makeInputRef(aggregate.getInput(), key);
- if (constants.containsKey(ref)) {
- map.put(key, constants.get(ref));
+ if (predicates.constantMap.containsKey(ref)) {
+ map.put(key, predicates.constantMap.get(ref));
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
index 53b4d42..80a1c57 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java
@@ -68,11 +68,8 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.regex.Pattern;
/**
@@ -299,8 +296,10 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
mq.getPulledUpPredicates(join.getLeft());
final RelOptPredicateList rightPredicates =
mq.getPulledUpPredicates(join.getRight());
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
final RelOptPredicateList predicates =
- leftPredicates.union(rightPredicates.shift(fieldCount));
+ leftPredicates.union(rexBuilder,
+ rightPredicates.shift(rexBuilder, fieldCount));
if (!reduceExpressions(join, expList, predicates, true)) {
return;
}
@@ -487,10 +486,8 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
final List<RexNode> constExps = Lists.newArrayList();
List<Boolean> addCasts = Lists.newArrayList();
final List<RexNode> removableCasts = Lists.newArrayList();
- final ImmutableMap<RexNode, RexNode> constants =
- predicateConstants(RexNode.class, rexBuilder, predicates);
- findReducibleExps(rel.getCluster().getTypeFactory(), expList, constants,
- constExps, addCasts, removableCasts);
+ findReducibleExps(rel.getCluster().getTypeFactory(), expList,
+ predicates.constantMap, constExps, addCasts, removableCasts);
if (constExps.isEmpty() && removableCasts.isEmpty()) {
return false;
}
@@ -519,11 +516,11 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
}
final List<RexNode> constExps2 = Lists.newArrayList(constExps);
- if (!constants.isEmpty()) {
+ if (!predicates.constantMap.isEmpty()) {
//noinspection unchecked
final List<Map.Entry<RexNode, RexNode>> pairs =
(List<Map.Entry<RexNode, RexNode>>) (List)
- Lists.newArrayList(constants.entrySet());
+ Lists.newArrayList(predicates.constantMap.entrySet());
RexReplacer replacer =
new RexReplacer(
rexBuilder,
@@ -615,152 +612,14 @@ public abstract class ReduceExpressionsRule extends RelOptRule {
* definition of constant, or {@link RexNode} to use
* {@link RexUtil#isConstant(RexNode)}
* @return Map from values to constants
- */
- public static <C extends RexNode> ImmutableMap<RexNode, C> predicateConstants(
- Class<C> clazz, RexBuilder rexBuilder, RelOptPredicateList predicates) {
- // We cannot use an ImmutableMap.Builder here. If there are multiple entries
- // with the same key (e.g. "WHERE deptno = 1 AND deptno = 2"), it doesn't
- // matter which we take, so the latter will replace the former.
- // The basic idea is to find all the pairs of RexNode = RexLiteral
- // (1) If 'predicates' contain a non-EQUALS, we bail out.
- // (2) It is OK if a RexNode is equal to the same RexLiteral several times,
- // (e.g. "WHERE deptno = 1 AND deptno = 1")
- // (3) It will return false if there are inconsistent constraints (e.g.
- // "WHERE deptno = 1 AND deptno = 2")
- final Map<RexNode, C> map = new HashMap<>();
- final Set<RexNode> excludeSet = new HashSet<>();
- for (RexNode predicate : predicates.pulledUpPredicates) {
- gatherConstraints(clazz, predicate, map, excludeSet, rexBuilder);
- }
- final ImmutableMap.Builder<RexNode, C> builder =
- ImmutableMap.builder();
- for (Map.Entry<RexNode, C> entry : map.entrySet()) {
- RexNode rexNode = entry.getKey();
- if (!overlap(rexNode, excludeSet)) {
- builder.put(rexNode, entry.getValue());
- }
- }
- return builder.build();
- }
-
- private static boolean overlap(RexNode rexNode, Set<RexNode> set) {
- if (rexNode instanceof RexCall) {
- for (RexNode r : ((RexCall) rexNode).getOperands()) {
- if (overlap(r, set)) {
- return true;
- }
- }
- return false;
- } else {
- return set.contains(rexNode);
- }
- }
-
- /** Tries to decompose the RexNode which is a RexCall into non-literal
- * RexNodes. */
- private static void decompose(Set<RexNode> set, RexNode rexNode) {
- if (rexNode instanceof RexCall) {
- for (RexNode r : ((RexCall) rexNode).getOperands()) {
- decompose(set, r);
- }
- } else if (!(rexNode instanceof RexLiteral)) {
- set.add(rexNode);
- }
- }
-
- 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();
- if (operands.size() != 2 && predicate.getKind() == SqlKind.EQUALS) {
- decompose(excludeSet, predicate);
- return;
- }
- // if it reaches here, we have rexNode equals rexNode
- final RexNode left;
- final RexNode right;
- if (predicate.getKind() == SqlKind.EQUALS) {
- left = operands.get(0);
- right = operands.get(1);
- } else {
- left = operands.get(0);
- right = rexBuilder.makeNullLiteral(left.getType().getSqlTypeName());
- }
- // note that literals are immutable too and they can only be compared through
- // values.
- gatherConstraint(clazz, left, right, map, excludeSet, rexBuilder);
- gatherConstraint(clazz, right, left, map, excludeSet, rexBuilder);
- }
-
- private static <C extends RexNode> void gatherConstraint(Class<C> clazz,
- RexNode left, RexNode right, Map<RexNode, C> map, Set<RexNode> excludeSet,
- RexBuilder rexBuilder) {
- if (!clazz.isInstance(right)) {
- return;
- }
- if (!RexUtil.isConstant(right)) {
- return;
- }
- C constant = clazz.cast(right);
- if (excludeSet.contains(left)) {
- return;
- }
- final C existedValue = map.get(left);
- if (existedValue == null) {
- switch (left.getKind()) {
- case CAST:
- // Convert "CAST(c) = literal" to "c = literal", as long as it is a
- // widening cast.
- final RexNode operand = ((RexCall) left).getOperands().get(0);
- if (canAssignFrom(left.getType(), operand.getType())) {
- final RexNode castRight =
- rexBuilder.makeCast(operand.getType(), constant);
- if (castRight instanceof RexLiteral) {
- left = operand;
- constant = clazz.cast(castRight);
- }
- }
- }
- map.put(left, constant);
- } else {
- if (existedValue instanceof RexLiteral
- && constant instanceof RexLiteral
- && !((RexLiteral) existedValue).getValue()
- .equals(((RexLiteral) constant).getValue())) {
- // we found conflicting values, e.g. left = 10 and left = 20
- map.remove(left);
- excludeSet.add(left);
- }
- }
- }
-
- /** Returns whether a value of {@code type2} can be assigned to a variable
- * of {@code type1}.
*
- * <p>For example:
- * <ul>
- * <li>{@code canAssignFrom(BIGINT, TINYINT)} returns {@code true}</li>
- * <li>{@code canAssignFrom(TINYINT, BIGINT)} returns {@code false}</li>
- * <li>{@code canAssignFrom(BIGINT, VARCHAR)} returns {@code false}</li>
- * </ul>
+ * @deprecated Use {@link RelOptPredicateList#constantMap}
*/
- private static boolean canAssignFrom(RelDataType type1, RelDataType type2) {
- final SqlTypeName name1 = type1.getSqlTypeName();
- final SqlTypeName name2 = type2.getSqlTypeName();
- if (name1.getFamily() == name2.getFamily()) {
- switch (name1.getFamily()) {
- case NUMERIC:
- return name1.compareTo(name2) >= 0;
- default:
- return true;
- }
- }
- return false;
+ @Deprecated // to be removed before 2.0
+ public static <C extends RexNode> ImmutableMap<RexNode, C> predicateConstants(
+ Class<C> clazz, RexBuilder rexBuilder, RelOptPredicateList predicates) {
+ return RexUtil.predicateConstants(clazz, rexBuilder,
+ predicates.pulledUpPredicates);
}
/** Pushes predicates into a CASE.
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
index b59b08d..39fd9e1 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
@@ -26,6 +26,7 @@ import org.apache.calcite.rel.core.Union;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.tools.RelBuilder;
@@ -73,14 +74,10 @@ public class UnionPullUpConstantsRule extends RelOptRule {
return;
}
- Map<RexNode, RexNode> conditionsExtracted =
- ReduceExpressionsRule.predicateConstants(RexNode.class, rexBuilder,
- predicates);
- Map<RexNode, RexNode> constants = new HashMap<>();
- for (int i = 0; i < count; i++) {
- RexNode expr = rexBuilder.makeInputRef(union, i);
- if (conditionsExtracted.containsKey(expr)) {
- constants.put(expr, conditionsExtracted.get(expr));
+ final Map<Integer, RexNode> constants = new HashMap<>();
+ for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) {
+ if (e.getKey() instanceof RexInputRef) {
+ constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue());
}
}
@@ -95,17 +92,17 @@ public class UnionPullUpConstantsRule extends RelOptRule {
List<String> topChildExprsFields = new ArrayList<>();
List<RexNode> refs = new ArrayList<>();
ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder();
- for (int i = 0; i < count; i++) {
- RexNode expr = rexBuilder.makeInputRef(union, i);
- RelDataTypeField field = fields.get(i);
- if (constants.containsKey(expr)) {
- topChildExprs.add(constants.get(expr));
+ for (RelDataTypeField field : fields) {
+ final RexNode constant = constants.get(field.getIndex());
+ if (constant != null) {
+ topChildExprs.add(constant);
topChildExprsFields.add(field.getName());
} else {
+ final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex());
topChildExprs.add(expr);
topChildExprsFields.add(field.getName());
refs.add(expr);
- refsIndexBuilder.set(i);
+ refsIndexBuilder.set(field.getIndex());
}
}
ImmutableBitSet refsIndex = refsIndexBuilder.build();
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
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 fd971bd..5766177 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -49,6 +49,7 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -309,6 +310,164 @@ public class RexUtil {
return false;
}
+ /** Creates a map containing each (e, constant) pair that occurs within
+ * a predicate list.
+ *
+ * @param clazz Class of expression that is considered constant
+ * @param rexBuilder Rex builder
+ * @param predicates Predicate list
+ * @param <C> what to consider a constant: {@link RexLiteral} to use a narrow
+ * definition of constant, or {@link RexNode} to use
+ * {@link RexUtil#isConstant(RexNode)}
+ * @return Map from values to constants
+ */
+ public static <C extends RexNode> ImmutableMap<RexNode, C> predicateConstants(
+ Class<C> clazz, RexBuilder rexBuilder, List<RexNode> predicates) {
+ // We cannot use an ImmutableMap.Builder here. If there are multiple entries
+ // with the same key (e.g. "WHERE deptno = 1 AND deptno = 2"), it doesn't
+ // matter which we take, so the latter will replace the former.
+ // The basic idea is to find all the pairs of RexNode = RexLiteral
+ // (1) If 'predicates' contain a non-EQUALS, we bail out.
+ // (2) It is OK if a RexNode is equal to the same RexLiteral several times,
+ // (e.g. "WHERE deptno = 1 AND deptno = 1")
+ // (3) It will return false if there are inconsistent constraints (e.g.
+ // "WHERE deptno = 1 AND deptno = 2")
+ final Map<RexNode, C> map = new HashMap<>();
+ final Set<RexNode> excludeSet = new HashSet<>();
+ for (RexNode predicate : predicates) {
+ gatherConstraints(clazz, predicate, map, excludeSet, rexBuilder);
+ }
+ final ImmutableMap.Builder<RexNode, C> builder =
+ ImmutableMap.builder();
+ for (Map.Entry<RexNode, C> entry : map.entrySet()) {
+ RexNode rexNode = entry.getKey();
+ if (!overlap(rexNode, excludeSet)) {
+ builder.put(rexNode, entry.getValue());
+ }
+ }
+ return builder.build();
+ }
+
+ private static boolean overlap(RexNode rexNode, Set<RexNode> set) {
+ if (rexNode instanceof RexCall) {
+ for (RexNode r : ((RexCall) rexNode).getOperands()) {
+ if (overlap(r, set)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return set.contains(rexNode);
+ }
+ }
+
+ /** Tries to decompose the RexNode which is a RexCall into non-literal
+ * RexNodes. */
+ private static void decompose(Set<RexNode> set, RexNode rexNode) {
+ if (rexNode instanceof RexCall) {
+ for (RexNode r : ((RexCall) rexNode).getOperands()) {
+ decompose(set, r);
+ }
+ } else if (!(rexNode instanceof RexLiteral)) {
+ set.add(rexNode);
+ }
+ }
+
+ 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();
+ if (operands.size() != 2 && predicate.getKind() == SqlKind.EQUALS) {
+ decompose(excludeSet, predicate);
+ return;
+ }
+ // if it reaches here, we have rexNode equals rexNode
+ final RexNode left;
+ final RexNode right;
+ if (predicate.getKind() == SqlKind.EQUALS) {
+ left = operands.get(0);
+ right = operands.get(1);
+ } else {
+ left = operands.get(0);
+ right = rexBuilder.makeNullLiteral(left.getType().getSqlTypeName());
+ }
+ // Note that literals are immutable too, and they can only be compared
+ // through values.
+ gatherConstraint(clazz, left, right, map, excludeSet, rexBuilder);
+ gatherConstraint(clazz, right, left, map, excludeSet, rexBuilder);
+ }
+
+ private static <C extends RexNode> void gatherConstraint(Class<C> clazz,
+ RexNode left, RexNode right, Map<RexNode, C> map, Set<RexNode> excludeSet,
+ RexBuilder rexBuilder) {
+ if (!clazz.isInstance(right)) {
+ return;
+ }
+ if (!isConstant(right)) {
+ return;
+ }
+ C constant = clazz.cast(right);
+ if (excludeSet.contains(left)) {
+ return;
+ }
+ final C existedValue = map.get(left);
+ if (existedValue == null) {
+ switch (left.getKind()) {
+ case CAST:
+ // Convert "CAST(c) = literal" to "c = literal", as long as it is a
+ // widening cast.
+ final RexNode operand = ((RexCall) left).getOperands().get(0);
+ if (canAssignFrom(left.getType(), operand.getType())) {
+ final RexNode castRight =
+ rexBuilder.makeCast(operand.getType(), constant);
+ if (castRight instanceof RexLiteral) {
+ left = operand;
+ constant = clazz.cast(castRight);
+ }
+ }
+ }
+ map.put(left, constant);
+ } else {
+ if (existedValue instanceof RexLiteral
+ && constant instanceof RexLiteral
+ && !((RexLiteral) existedValue).getValue()
+ .equals(((RexLiteral) constant).getValue())) {
+ // we found conflicting values, e.g. left = 10 and left = 20
+ map.remove(left);
+ excludeSet.add(left);
+ }
+ }
+ }
+
+ /** Returns whether a value of {@code type2} can be assigned to a variable
+ * of {@code type1}.
+ *
+ * <p>For example:
+ * <ul>
+ * <li>{@code canAssignFrom(BIGINT, TINYINT)} returns {@code true}</li>
+ * <li>{@code canAssignFrom(TINYINT, BIGINT)} returns {@code false}</li>
+ * <li>{@code canAssignFrom(BIGINT, VARCHAR)} returns {@code false}</li>
+ * </ul>
+ */
+ private static boolean canAssignFrom(RelDataType type1, RelDataType type2) {
+ final SqlTypeName name1 = type1.getSqlTypeName();
+ final SqlTypeName name2 = type2.getSqlTypeName();
+ if (name1.getFamily() == name2.getFamily()) {
+ switch (name1.getFamily()) {
+ case NUMERIC:
+ return name1.compareTo(name2) >= 0;
+ default:
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Walks over an expression and determines whether it is constant.
*/
http://git-wip-us.apache.org/repos/asf/calcite/blob/0f36aec7/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
----------------------------------------------------------------------
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 9481d2f..641346d 100644
--- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java
@@ -44,6 +44,7 @@ import org.apache.calcite.util.TestUtil;
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
@@ -56,6 +57,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
@@ -1186,6 +1189,64 @@ public class RexProgramTest {
c.set(Calendar.SECOND, s);
return c;
}
+
+ @Test public void testConstantMap() {
+ final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);
+ final RelDataType rowType = typeFactory.builder()
+ .add("a", intType)
+ .add("b", intType)
+ .add("c", intType)
+ .add("d", intType)
+ .add("e", intType)
+ .build();
+
+ final RexDynamicParam range = rexBuilder.makeDynamicParam(rowType, 0);
+ final RexNode aRef = rexBuilder.makeFieldAccess(range, 0);
+ final RexNode bRef = rexBuilder.makeFieldAccess(range, 1);
+ final RexNode cRef = rexBuilder.makeFieldAccess(range, 2);
+ final RexNode dRef = rexBuilder.makeFieldAccess(range, 3);
+ final RexNode eRef = rexBuilder.makeFieldAccess(range, 4);
+ final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE);
+ final RexLiteral literal2 = rexBuilder.makeExactLiteral(BigDecimal.valueOf(2));
+
+ final ImmutableMap<RexNode, RexNode> map =
+ RexUtil.predicateConstants(RexNode.class, rexBuilder,
+ ImmutableList.of(eq(aRef, bRef),
+ eq(cRef, literal1),
+ eq(cRef, aRef),
+ eq(dRef, eRef)));
+ assertThat(getString(map),
+ is("{1=?0.c, ?0.a=?0.b, ?0.b=?0.a, ?0.c=1, ?0.d=?0.e, ?0.e=?0.d}"));
+
+ // Contradictory constraints yield no constants
+ final RexNode ref0 = rexBuilder.makeInputRef(rowType, 0);
+ final RexNode ref1 = rexBuilder.makeInputRef(rowType, 1);
+ final ImmutableMap<RexNode, RexNode> map2 =
+ RexUtil.predicateConstants(RexNode.class, rexBuilder,
+ ImmutableList.of(eq(ref0, literal1),
+ eq(ref0, literal2)));
+ assertThat(getString(map2), is("{}"));
+
+ // Contradictory constraints on field accesses SHOULD yield no constants
+ // but currently there's a bug
+ final ImmutableMap<RexNode, RexNode> map3 =
+ RexUtil.predicateConstants(RexNode.class, rexBuilder,
+ ImmutableList.of(eq(aRef, literal1),
+ eq(aRef, literal2)));
+ assertThat(getString(map3), is("{1=?0.a, 2=?0.a}"));
+
+
+ }
+
+ /** Converts a map to a string, sorting on the string representation of its
+ * keys. */
+ private static String getString(ImmutableMap<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());
+ }
+ return map2.toString();
+ }
}
// End RexProgramTest.java