diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java index 5a93a4b99f5f..6005f1c4599e 100644 --- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java +++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java @@ -70,17 +70,7 @@ import java.io.PrintWriter; import java.io.StringWriter; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.PriorityQueue; -import java.util.Set; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -1107,13 +1097,17 @@ private boolean fixUpInputs(RelNode rel) { assert input instanceof RelSubset; final RelSubset subset = (RelSubset) input; RelSubset newSubset = canonize(subset); - newInputs.add(newSubset); - if (newSubset != subset) { - if (subset.set != newSubset.set) { - subset.set.parents.remove(rel); - newSubset.set.parents.add(rel); + if (hasCycle(rel,newSubset)) { + newInputs.add(input); + } else { + newInputs.add(newSubset); + if (newSubset != subset) { + if (subset.set != newSubset.set) { + subset.set.parents.remove(rel); + newSubset.set.parents.add(rel); + } + changeCount++; } - changeCount++; } } @@ -1122,7 +1116,8 @@ private boolean fixUpInputs(RelNode rel) { RelNode removed = mapDigestToRel.remove(rel.getRelDigest()); assert removed == rel; for (int i = 0; i < inputs.size(); i++) { - rel.replaceInput(i, newInputs.get(i)); + RelNode newInput = newInputs.get(i); + rel.replaceInput(i, newInput); } rel.recomputeDigest(); return true; @@ -1130,6 +1125,37 @@ private boolean fixUpInputs(RelNode rel) { return false; } + private boolean hasCycle(RelNode rel, RelNode newInput) { + Stack visited = new Stack<>(); + visited.push(rel.getId()); + return hasCycle(newInput,visited); + } + + private boolean hasCycle(RelNode node, Stack visited) { + if (node instanceof RelSubset) { + RelSubset subset = (RelSubset) node; + node = subset.getBestOrOriginal(); + } + if ( visited.contains(node.getId()) ) { + return true; + } + try { + visited.push(node.getId()); + List inputs = node.getInputs(); + for (RelNode input : inputs) { + boolean cycle = hasCycle(input, visited); + if ( cycle) { + return true; + } + } + } finally { + visited.pop(); + } + return false; + } + + + private RelSet merge(RelSet set1, RelSet set2) { assert set1 != set2 : "pre: set1 != set2";