chunweilei commented on code in PR #2761:
URL: https://github.com/apache/calcite/pull/2761#discussion_r876568181
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
+ if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
+ final RexInputRef in1 = (RexInputRef) op1;
+ final RexInputRef in2 = (RexInputRef) op2;
+ Set<Integer> set = null;
+ for (Set<Integer> s : equalSets) {
+ if (s.contains(in1.getIndex()) || s.contains(in2.getIndex())) {
+ set = s;
+ break;
+ }
+ }
+ if (set == null) {
+ set = new LinkedHashSet<>(); // to make the result deterministic
+ equalSets.add(set);
+ }
+ set.add(in1.getIndex());
+ set.add(in2.getIndex());
+ } else {
+ result.add(rexNode);
+ }
+ } else {
+ result.add(rexNode);
+ }
+ }
+
+ boolean needOptimize = false;
+ for (Set<Integer> set : equalSets) {
+ if (set.size() > 2) {
+ needOptimize = true;
+ break;
+ }
+ }
+ if (!needOptimize) {
+ // keep the conditions unchanged.
+ return;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ for (Set<Integer> set : equalSets) {
+ final List<Integer> leftSet = new ArrayList<>();
+ final List<Integer> rightSet = new ArrayList<>();
+ for (int i : set) {
+ if (i < join.getLeft().getRowType().getFieldCount()) {
+ leftSet.add(i);
+ } else {
+ rightSet.add(i);
+ }
+ }
+ // add left side conditions
+ if (leftSet.size() > 1) {
+ for (int i = 1; i < leftSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, leftSet.get(i))));
+ }
+ }
+ // add right side conditions
+ if (rightSet.size() > 1) {
+ for (int i = 1; i < rightSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, rightSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(i))));
+ }
+ }
+ // only need one equal condition for each equal set
+ if (leftSet.size() > 0 && rightSet.size() > 0) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(0))));
+ }
+ }
+
+ rexNodes.clear();
+ rexNodes.addAll(result);
+ }
Review Comment:
It is not a good practice to change the parameter. It'd be better to return
a new List.
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
Review Comment:
It is not a good practice to change the parameter. It'd be better to return
a new List.
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
+ if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
+ final RexInputRef in1 = (RexInputRef) op1;
+ final RexInputRef in2 = (RexInputRef) op2;
+ Set<Integer> set = null;
+ for (Set<Integer> s : equalSets) {
+ if (s.contains(in1.getIndex()) || s.contains(in2.getIndex())) {
+ set = s;
+ break;
+ }
+ }
+ if (set == null) {
+ set = new LinkedHashSet<>(); // to make the result deterministic
+ equalSets.add(set);
+ }
+ set.add(in1.getIndex());
+ set.add(in2.getIndex());
+ } else {
+ result.add(rexNode);
+ }
+ } else {
+ result.add(rexNode);
+ }
+ }
+
+ boolean needOptimize = false;
+ for (Set<Integer> set : equalSets) {
+ if (set.size() > 2) {
+ needOptimize = true;
+ break;
+ }
+ }
+ if (!needOptimize) {
+ // keep the conditions unchanged.
+ return;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ for (Set<Integer> set : equalSets) {
+ final List<Integer> leftSet = new ArrayList<>();
+ final List<Integer> rightSet = new ArrayList<>();
+ for (int i : set) {
+ if (i < join.getLeft().getRowType().getFieldCount()) {
+ leftSet.add(i);
+ } else {
+ rightSet.add(i);
+ }
+ }
+ // add left side conditions
+ if (leftSet.size() > 1) {
+ for (int i = 1; i < leftSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, leftSet.get(i))));
+ }
+ }
+ // add right side conditions
+ if (rightSet.size() > 1) {
+ for (int i = 1; i < rightSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, rightSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(i))));
+ }
+ }
+ // only need one equal condition for each equal set
+ if (leftSet.size() > 0 && rightSet.size() > 0) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(0))));
+ }
+ }
Review Comment:
The function is a little long. Can we split it into serval functions?
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
+ if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
+ final RexInputRef in1 = (RexInputRef) op1;
+ final RexInputRef in2 = (RexInputRef) op2;
+ Set<Integer> set = null;
+ for (Set<Integer> s : equalSets) {
+ if (s.contains(in1.getIndex()) || s.contains(in2.getIndex())) {
+ set = s;
+ break;
+ }
+ }
+ if (set == null) {
+ set = new LinkedHashSet<>(); // to make the result deterministic
+ equalSets.add(set);
+ }
+ set.add(in1.getIndex());
+ set.add(in2.getIndex());
+ } else {
+ result.add(rexNode);
+ }
+ } else {
+ result.add(rexNode);
+ }
+ }
+
+ boolean needOptimize = false;
+ for (Set<Integer> set : equalSets) {
+ if (set.size() > 2) {
+ needOptimize = true;
+ break;
+ }
+ }
+ if (!needOptimize) {
+ // keep the conditions unchanged.
+ return;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ for (Set<Integer> set : equalSets) {
+ final List<Integer> leftSet = new ArrayList<>();
+ final List<Integer> rightSet = new ArrayList<>();
+ for (int i : set) {
+ if (i < join.getLeft().getRowType().getFieldCount()) {
+ leftSet.add(i);
+ } else {
+ rightSet.add(i);
+ }
+ }
+ // add left side conditions
+ if (leftSet.size() > 1) {
+ for (int i = 1; i < leftSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, leftSet.get(i))));
+ }
+ }
+ // add right side conditions
+ if (rightSet.size() > 1) {
+ for (int i = 1; i < rightSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, rightSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(i))));
+ }
+ }
+ // only need one equal condition for each equal set
+ if (leftSet.size() > 0 && rightSet.size() > 0) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(0))));
+ }
+ }
+
+ rexNodes.clear();
+ rexNodes.addAll(result);
+ }
Review Comment:
It is not a good practice to change the parameter. It'd be better to return
a new List.
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
+ if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
+ final RexInputRef in1 = (RexInputRef) op1;
+ final RexInputRef in2 = (RexInputRef) op2;
+ Set<Integer> set = null;
+ for (Set<Integer> s : equalSets) {
+ if (s.contains(in1.getIndex()) || s.contains(in2.getIndex())) {
+ set = s;
+ break;
+ }
+ }
+ if (set == null) {
+ set = new LinkedHashSet<>(); // to make the result deterministic
+ equalSets.add(set);
+ }
+ set.add(in1.getIndex());
+ set.add(in2.getIndex());
+ } else {
+ result.add(rexNode);
+ }
+ } else {
+ result.add(rexNode);
+ }
+ }
+
+ boolean needOptimize = false;
+ for (Set<Integer> set : equalSets) {
+ if (set.size() > 2) {
+ needOptimize = true;
+ break;
+ }
+ }
+ if (!needOptimize) {
+ // keep the conditions unchanged.
+ return;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ for (Set<Integer> set : equalSets) {
+ final List<Integer> leftSet = new ArrayList<>();
+ final List<Integer> rightSet = new ArrayList<>();
+ for (int i : set) {
+ if (i < join.getLeft().getRowType().getFieldCount()) {
+ leftSet.add(i);
Review Comment:
Use a variable to represent `join.getLeft().getRowType().getFieldCount()` so
that we can move it out of the loop.
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
+ if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
+ final RexInputRef in1 = (RexInputRef) op1;
+ final RexInputRef in2 = (RexInputRef) op2;
+ Set<Integer> set = null;
+ for (Set<Integer> s : equalSets) {
+ if (s.contains(in1.getIndex()) || s.contains(in2.getIndex())) {
+ set = s;
+ break;
+ }
+ }
+ if (set == null) {
+ set = new LinkedHashSet<>(); // to make the result deterministic
+ equalSets.add(set);
+ }
+ set.add(in1.getIndex());
+ set.add(in2.getIndex());
+ } else {
+ result.add(rexNode);
+ }
+ } else {
+ result.add(rexNode);
+ }
+ }
+
+ boolean needOptimize = false;
+ for (Set<Integer> set : equalSets) {
+ if (set.size() > 2) {
+ needOptimize = true;
+ break;
+ }
+ }
+ if (!needOptimize) {
+ // keep the conditions unchanged.
+ return;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ for (Set<Integer> set : equalSets) {
+ final List<Integer> leftSet = new ArrayList<>();
+ final List<Integer> rightSet = new ArrayList<>();
+ for (int i : set) {
+ if (i < join.getLeft().getRowType().getFieldCount()) {
+ leftSet.add(i);
Review Comment:
Use a variable to represent `join.getLeft().getRowType().getFieldCount()` so
that we can move it out of the loop.
##########
core/src/main/java/org/apache/calcite/rel/rules/FilterJoinRule.java:
##########
@@ -225,6 +232,105 @@ protected void perform(RelOptRuleCall call, @Nullable
Filter filter,
call.transformTo(relBuilder.build());
}
+ /**
+ * Infer more equal conditions for the Join Condition.
+ *
+ * <p> For example, in {@code SELECT * FROM T1, T2, T3 WHERE T1.id = T3.id
AND T2.id = T3.id},
+ * we can infer {@code T1.id = T2.id} for the first Join node from second
Join node's condition:
+ * {@code T1.id = T3.id AND T2.id = T3.id}.
+ *
+ * <p>For the above SQL, the second Join's condition is {@code T1.id = T3.id
AND T2.id = T3.id}.
+ * After inference, the final condition would be: {@code T1.id = T2.id AND
T1.id = T3.id}, the
+ * {@code T1.id = T2.id} can be further pushed into LHS.
+ *
+ * @param rexNodes the Join condition
+ * @param join the Join node
+ */
+ protected void inferJoinEqualConditions(List<RexNode> rexNodes, Join join) {
+ final List<Set<Integer>> equalSets = new ArrayList<>();
+ final List<RexNode> result = new ArrayList<>(rexNodes.size());
+ for (RexNode rexNode : rexNodes) {
+ if (rexNode.isA(SqlKind.EQUALS)) {
+ final RexNode op1 = ((RexCall) rexNode).getOperands().get(0);
+ final RexNode op2 = ((RexCall) rexNode).getOperands().get(1);
+ if (op1 instanceof RexInputRef && op2 instanceof RexInputRef) {
+ final RexInputRef in1 = (RexInputRef) op1;
+ final RexInputRef in2 = (RexInputRef) op2;
+ Set<Integer> set = null;
+ for (Set<Integer> s : equalSets) {
+ if (s.contains(in1.getIndex()) || s.contains(in2.getIndex())) {
+ set = s;
+ break;
+ }
+ }
+ if (set == null) {
+ set = new LinkedHashSet<>(); // to make the result deterministic
+ equalSets.add(set);
+ }
+ set.add(in1.getIndex());
+ set.add(in2.getIndex());
+ } else {
+ result.add(rexNode);
+ }
+ } else {
+ result.add(rexNode);
+ }
+ }
+
+ boolean needOptimize = false;
+ for (Set<Integer> set : equalSets) {
+ if (set.size() > 2) {
+ needOptimize = true;
+ break;
+ }
+ }
+ if (!needOptimize) {
+ // keep the conditions unchanged.
+ return;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ for (Set<Integer> set : equalSets) {
+ final List<Integer> leftSet = new ArrayList<>();
+ final List<Integer> rightSet = new ArrayList<>();
+ for (int i : set) {
+ if (i < join.getLeft().getRowType().getFieldCount()) {
+ leftSet.add(i);
+ } else {
+ rightSet.add(i);
+ }
+ }
+ // add left side conditions
+ if (leftSet.size() > 1) {
+ for (int i = 1; i < leftSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, leftSet.get(i))));
+ }
+ }
+ // add right side conditions
+ if (rightSet.size() > 1) {
+ for (int i = 1; i < rightSet.size(); ++i) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, rightSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(i))));
+ }
+ }
+ // only need one equal condition for each equal set
+ if (leftSet.size() > 0 && rightSet.size() > 0) {
+ result.add(
+ rexBuilder.makeCall(SqlStdOperatorTable.EQUALS,
+ rexBuilder.makeInputRef(join, leftSet.get(0)),
+ rexBuilder.makeInputRef(join, rightSet.get(0))));
+ }
+ }
Review Comment:
The function is a little long. Can we split it into serval functions?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]