[ 
https://issues.apache.org/jira/browse/CALCITE-5260?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

ASF GitHub Bot updated CALCITE-5260:
------------------------------------
    Labels: pull-request-available  (was: )

> The bindings should properly pop last added RelNode when fail to match a 
> subtree
> --------------------------------------------------------------------------------
>
>                 Key: CALCITE-5260
>                 URL: https://issues.apache.org/jira/browse/CALCITE-5260
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 1.32.0
>            Reporter: mengdou
>            Assignee: mengdou
>            Priority: Minor
>              Labels: pull-request-available
>         Attachments: image-2022-09-01-19-34-04-600.png, 
> image-2022-09-01-19-36-29-325.png, image-2022-09-01-19-41-48-852.png, 
> image-2022-09-01-19-42-08-804.png
>
>          Time Spent: 10m
>  Remaining Estimate: 0h
>
> First I define a pattern(For demonstration)  like this:
> {code:java}
> // pattern like this, using 'unordered' child policy for MultiJoin
> operandJ(MultiJoin.class, ...
>   unordered(
>     operand(Project.class, ...
>       operand(TableScan.class, ...))))
> {code}
>  
> Then there is a RelNode tree like this:
> {code:java}
> MultiJoin
>   Project
>     Filter  -- matched unsuccessfully and jump out
>       TableScan
>   Project
>     TableScan -- matched successfully{code}
>  
> And HepPlanner adds temporary nodes into 'bindings' list when applying a rule 
> and trying to match operands. When fail to match a subtree, HepPlanner would 
> jump out immediately and try other possible subtrees, but DO NOT pop the last 
> added node:
>  
> {code:java}
> private static boolean matchOperands(
>     RelOptRuleOperand operand,
>     RelNode rel,
>     List<RelNode> bindings,                           // To store temporary 
> matched nodes when applying a rule
>     Map<RelNode, List<RelNode>> nodeChildren) {
>   if (!operand.matches(rel)) {
>     return false;
>   }
>   for (RelNode input : rel.getInputs()) {
>     if (!(input instanceof HepRelVertex)) {
>       // The graph could be partially optimized for materialized view. In that
>       // case, the input would be a RelNode and shouldn't be matched again 
> here.
>       return false;
>     }
>   }
>   bindings.add(rel);                            // Add a matched nodes when 
> applying a rule
>   @SuppressWarnings("unchecked")
>   List<HepRelVertex> childRels = (List) rel.getInputs();
>   switch (operand.childPolicy) {
>   case ANY:
>     return true;
>   case UNORDERED:
>     // For each operand, at least one child must match. If
>     // matchAnyChildren, usually there's just one operand.
>     for (RelOptRuleOperand childOperand : operand.getChildOperands()) {
>       boolean match = false;
>       for (HepRelVertex childRel : childRels) {
>         match =
>             matchOperands(
>                 childOperand,
>                 childRel.getCurrentRel(),
>                 bindings,
>                 nodeChildren);
>         if (match) {
>           break;
>         }
>       }
>       if (!match) {
>         return false;                // If failed to match, it just returns 
> false but not pops the last item added in the bindings 
>       }
>     }
>     final List<RelNode> children = new ArrayList<>(childRels.size());
>     for (HepRelVertex childRel : childRels) {
>       children.add(childRel.getCurrentRel());
>     }
>     nodeChildren.put(rel, children);
>     return true;
>   default:
>     int n = operand.getChildOperands().size();
>     if (childRels.size() < n) {
>       return false;
>     }
>     for (Pair<HepRelVertex, RelOptRuleOperand> pair
>         : Pair.zip(childRels, operand.getChildOperands())) {
>       boolean match =
>           matchOperands(
>               pair.right,
>               pair.left.getCurrentRel(),
>               bindings,
>               nodeChildren);
>       if (!match) {
>         return false;
>       }
>     }
>     return true;
>   }
> }
> {code}
>  
>  
> If HepPlanner matches ANOTHER subtree for current rule finally, it will 
> constructs a HepRuleCall using bindings as the matched relnodes, which 
> contains unnecessary nodes yet, and the assertion fails.
> {code:java}
> ......
> boolean match =
>     matchOperands(
>         rule.getOperand(),
>         vertex.getCurrentRel(),
>         bindings,
>         nodeChildren);
> if (!match) {
>   return null;
> }
> HepRuleCall call =
>     new HepRuleCall(
>         this,
>         rule.getOperand(),
>         bindings.toArray(new RelNode[0]),      // Use the bindings, which 
> contains unnecessary relnodes, to construct a HepRuleCall
>         nodeChildren,
>         parents);
> // Allow the rule to apply its own side-conditions.
> if (!rule.matches(call)) {
>   return null;
> }
> fireRule(call);
> ......{code}
> And an assertion in constructor fails  
> {code:java}
> /**
>  * Creates a RelOptRuleCall.
>  *
>  * @param planner      Planner
>  * @param operand      Root operand
>  * @param rels         Array of relational expressions which matched each
>  *                     operand
>  * @param nodeInputs   For each node which matched with
>  *                     {@code matchAnyChildren}=true, a list of the node's
>  *                     inputs
>  * @param parents      list of parent RelNodes corresponding to the first
>  *                     relational expression in the array argument, if known;
>  *                     otherwise, null
>  */
> protected RelOptRuleCall(
>     RelOptPlanner planner,
>     RelOptRuleOperand operand,
>     RelNode[] rels,
>     Map<RelNode, List<RelNode>> nodeInputs,
>     @Nullable List<RelNode> parents) {
>   this.id = nextId++;
>   this.planner = planner;
>   this.operand0 = operand;
>   this.nodeInputs = nodeInputs;
>   this.rule = operand.getRule();
>   this.rels = rels;
>   this.parents = parents;
> // Assertion fails because of the length of rels(the same as bindings) is 
> greater than the length of rules.operands
>    assert rels.length == rule.operands.size();    
> }
>  {code}
>  



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to