in core/src/main/java/org/apache/calcite/plan/volcano/VolcanoRuleCall.java [270:413]
private void matchRecurse(int solve) {
assert solve > 0;
assert solve <= rule.operands.size();
final List<RelOptRuleOperand> operands = getRule().operands;
if (solve == operands.size()) {
// We have matched all operands. Now ask the rule whether it
// matches; this gives the rule chance to apply side-conditions.
// If the side-conditions are satisfied, we have a match.
if (getRule().matches(this)) {
onMatch();
}
} else {
final int[] solveOrder = castNonNull(operand0.solveOrder);
final int operandOrdinal = solveOrder[solve];
final int previousOperandOrdinal = solveOrder[solve - 1];
boolean ascending = operandOrdinal < previousOperandOrdinal;
final RelOptRuleOperand previousOperand =
operands.get(previousOperandOrdinal);
final RelOptRuleOperand operand = operands.get(operandOrdinal);
final RelNode previous = rels[previousOperandOrdinal];
final RelOptRuleOperand parentOperand;
final Collection<? extends RelNode> successors;
if (ascending) {
assert previousOperand.getParent() == operand;
assert operand.getMatchedClass() != RelSubset.class;
if (previousOperand.getMatchedClass() != RelSubset.class
&& previous instanceof RelSubset) {
throw new RuntimeException("RelSubset should not match with "
+ previousOperand.getMatchedClass().getSimpleName());
}
parentOperand = operand;
final RelSubset subset = volcanoPlanner.getSubsetNonNull(previous);
successors = subset.getParentRels();
} else {
parentOperand =
requireNonNull(operand.getParent(),
() -> "operand.getParent() for " + operand);
final RelNode parentRel = rels[parentOperand.ordinalInRule];
final List<RelNode> inputs = parentRel.getInputs();
// if the child is unordered, then add all rels in all input subsets to the successors list
// because unordered can match child in any ordinal
if (parentOperand.childPolicy == RelOptRuleOperandChildPolicy.UNORDERED) {
if (operand.getMatchedClass() == RelSubset.class) {
// Find all the sibling subsets that satisfy this subset's traitSet
successors = inputs.stream()
.flatMap(subset -> ((RelSubset) subset).getSubsetsSatisfyingThis())
.collect(Collectors.toList());
} else {
List<RelNode> allRelsInAllSubsets = new ArrayList<>();
Set<RelNode> duplicates = new HashSet<>();
for (RelNode input : inputs) {
if (!duplicates.add(input)) {
// Ignore duplicate subsets
continue;
}
RelSubset inputSubset = (RelSubset) input;
for (RelNode rel : inputSubset.getRels()) {
if (!duplicates.add(rel)) {
// Ignore duplicate relations
continue;
}
allRelsInAllSubsets.add(rel);
}
}
successors = allRelsInAllSubsets;
}
} else if (operand.ordinalInParent < inputs.size()) {
// child policy is not unordered
// we need to find the exact input node based on child operand's ordinalInParent
final RelSubset subset =
(RelSubset) inputs.get(operand.ordinalInParent);
if (operand.getMatchedClass() == RelSubset.class) {
// Find all the sibling subsets that satisfy this subset'straitSet
successors =
subset.getSubsetsSatisfyingThis().collect(Collectors.toList());
} else {
successors = subset.getRelList();
}
} else {
// The operand expects parentRel to have a certain number
// of inputs and it does not.
successors = ImmutableList.of();
}
}
for (RelNode rel : successors) {
if (operand.getRule() instanceof TransformationRule
&& rel.getConvention() != previous.getConvention()) {
continue;
}
if (!operand.matches(rel)) {
continue;
}
if (ascending && operand.childPolicy != RelOptRuleOperandChildPolicy.UNORDERED) {
// We know that the previous operand was *a* child of its parent,
// but now check that it is the *correct* child.
if (previousOperand.ordinalInParent >= rel.getInputs().size()) {
continue;
}
final RelSubset input =
(RelSubset) rel.getInput(previousOperand.ordinalInParent);
if (previousOperand.getMatchedClass() == RelSubset.class) {
// The matched subset (previous) should satisfy our input subset (input)
if (input.getSubsetsSatisfyingThis().noneMatch(previous::equals)) {
continue;
}
} else {
if (!input.contains(previous)) {
continue;
}
}
}
// Assign "childRels" if the operand is UNORDERED.
switch (parentOperand.childPolicy) {
case UNORDERED:
// Note: below is ill-defined. Suppose there's a union with 3 inputs,
// and the rule is written as Union.class, unordered(...)
// What should be provided for the rest 2 arguments?
// RelSubsets? Random relations from those subsets?
// For now, Calcite code does not use getChildRels, so the bug is just waiting its day
if (ascending) {
final List<RelNode> inputs = Lists.newArrayList(rel.getInputs());
inputs.set(previousOperand.ordinalInParent, previous);
setChildRels(rel, inputs);
} else {
List<RelNode> inputs = getChildRels(previous);
if (inputs == null) {
inputs = Lists.newArrayList(previous.getInputs());
}
inputs.set(operand.ordinalInParent, rel);
setChildRels(previous, inputs);
}
break;
default:
break;
}
rels[operandOrdinal] = rel;
matchRecurse(solve + 1);
}
}
}