HepInstruction.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to you under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.calcite.plan.hep;

import org.apache.calcite.plan.RelOptRule;

import com.google.common.collect.ImmutableList;

import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.apache.calcite.linq4j.Nullness.castNonNull;

import static java.util.Objects.requireNonNull;

/**
 * HepInstruction represents one instruction in a HepProgram. The actual
 * instruction set is defined here via inner classes; if these grow too big,
 * they should be moved out to top-level classes.
 */
abstract class HepInstruction {
  //~ Methods ----------------------------------------------------------------

  /** Creates runtime state for this instruction.
   *
   * <p>The state is mutable, knows how to execute the instruction, and is
   * discarded after this execution. See {@link HepState}.
   *
   * @param px Preparation context; the state should copy from the context
   * all information that it will need to execute
   *
   * @return Initialized state
   */
  abstract HepState prepare(PrepareContext px);

  //~ Inner Classes ----------------------------------------------------------

  /** Instruction that executes all rules of a given class. */
  static class RuleClass extends HepInstruction {
    final Class<? extends RelOptRule> ruleClass;

    <R extends RelOptRule> RuleClass(Class<R> ruleClass) {
      this.ruleClass = requireNonNull(ruleClass, "ruleClass");
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link RuleClass} instruction. */
    class State extends HepState {
      /** Actual rule set instantiated during planning by filtering all the
       * planner's rules through {@link #ruleClass}. */
      @Nullable Set<RelOptRule> ruleSet;

      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeRuleClass(RuleClass.this, this);
      }
    }
  }

  /** Instruction that executes all rules in a given collection. */
  static class RuleCollection extends HepInstruction {
    /** Collection of rules to apply. */
    final List<RelOptRule> rules;

    RuleCollection(Collection<RelOptRule> rules) {
      this.rules = ImmutableList.copyOf(rules);
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link RuleCollection} instruction. */
    class State extends HepState {
      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeRuleCollection(RuleCollection.this, this);
      }
    }
  }

  /** Instruction that executes converter rules. */
  static class ConverterRules extends HepInstruction {
    final boolean guaranteed;

    ConverterRules(boolean guaranteed) {
      this.guaranteed = guaranteed;
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link ConverterRules} instruction. */
    class State extends HepState {
      /** Actual rule set instantiated during planning by filtering all the
       * planner's rules, looking for the desired converters. */
      @MonotonicNonNull Set<RelOptRule> ruleSet;

      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeConverterRules(ConverterRules.this, this);
      }
    }
  }

  /** Instruction that finds common relational sub-expressions. */
  static class CommonRelSubExprRules extends HepInstruction {
    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link CommonRelSubExprRules} instruction. */
    class State extends HepState {
      @Nullable Set<RelOptRule> ruleSet;

      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeCommonRelSubExprRules(CommonRelSubExprRules.this, this);
      }
    }
  }

  /** Instruction that executes a given rule. */
  static class RuleInstance extends HepInstruction {
    /** Explicitly specified rule. */
    final RelOptRule rule;

    RuleInstance(RelOptRule rule) {
      this.rule = requireNonNull(rule, "rule");
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link RuleInstance} instruction. */
    class State extends HepState {
      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeRuleInstance(RuleInstance.this, this);
      }
    }
  }

  /** Instruction that executes a rule that is looked up by description. */
  static class RuleLookup extends HepInstruction {
    /** Description to look for. */
    final String ruleDescription;

    RuleLookup(String ruleDescription) {
      this.ruleDescription = requireNonNull(ruleDescription, "ruleDescription");
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link RuleLookup} instruction. */
    class State extends HepState {
      /** Rule looked up by planner from description. */
      @Nullable RelOptRule rule;

      State(PrepareContext px) {
        super(px);
      }

      @Override void init() {
        // Look up anew each run.
        rule = null;
      }

      @Override void execute() {
        planner.executeRuleLookup(RuleLookup.this, this);
      }
    }
  }

  /** Instruction that sets match order. */
  static class MatchOrder extends HepInstruction {
    final HepMatchOrder order;

    MatchOrder(HepMatchOrder order) {
      this.order = requireNonNull(order, "order");
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link MatchOrder} instruction. */
    class State extends HepState {
      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeMatchOrder(MatchOrder.this, this);
      }
    }
  }

  /** Instruction that sets match limit. */
  static class MatchLimit extends HepInstruction {
    final int limit;

    MatchLimit(int limit) {
      this.limit = limit;
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link MatchLimit} instruction. */
    class State extends HepState {
      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeMatchLimit(MatchLimit.this, this);
      }
    }
  }

  /** Instruction that executes a sub-program. */
  static class SubProgram extends HepInstruction {
    final HepProgram subProgram;

    SubProgram(HepProgram subProgram) {
      this.subProgram = requireNonNull(subProgram, "subProgram");
    }

    @Override HepProgram.State prepare(PrepareContext px) {
      return subProgram.prepare(px);
    }

    /** State for a {@link SubProgram} instruction. */
    class State extends HepState {
      final HepProgram.State subProgramState;

      State(PrepareContext px) {
        super(px);
        subProgramState = subProgram.prepare(px);
      }

      @Override void init() {
        subProgramState.init();
      }

      @Override void execute() {
        planner.executeSubProgram(SubProgram.this, this);
      }
    }
  }

  /** Instruction that begins a group. */
  static class BeginGroup extends HepInstruction {
    final EndGroup endGroup;

    BeginGroup(EndGroup endGroup) {
      this.endGroup = requireNonNull(endGroup, "endGroup");
    }

    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link BeginGroup} instruction. */
    class State extends HepState {
      final HepInstruction.EndGroup.State endGroup;

      State(PrepareContext px) {
        super(px);
        this.endGroup = requireNonNull(px.endGroupState, "endGroupState");
      }

      @Override void execute() {
        planner.executeBeginGroup(BeginGroup.this, this);
      }
    }
  }

  /** Placeholder instruction that marks the beginning of a group under
   * construction. */
  static class Placeholder extends HepInstruction {
    @Override HepState prepare(PrepareContext px) {
      throw new UnsupportedOperationException();
    }
  }

  /** Instruction that ends a group. */
  static class EndGroup extends HepInstruction {
    @Override State prepare(PrepareContext px) {
      return new State(px);
    }

    /** State for a {@link EndGroup} instruction. */
    class State extends HepState {
      /** Actual rule set instantiated during planning by collecting grouped
       * rules. */
      final Set<RelOptRule> ruleSet = new HashSet<>();

      boolean collecting = true;

      State(PrepareContext px) {
        super(px);
      }

      @Override void execute() {
        planner.executeEndGroup(EndGroup.this, this);
      }

      @Override void init() {
        collecting = true;
      }
    }
  }

  /** All the information that might be necessary to initialize {@link HepState}
   * for a particular instruction. */
  static class PrepareContext {
    final HepPlanner planner;
    final HepProgram.State programState;
    final EndGroup.State endGroupState;

    private PrepareContext(HepPlanner planner,
        HepProgram.State programState, EndGroup.State endGroupState) {
      this.planner = planner;
      this.programState = programState;
      this.endGroupState = endGroupState;
    }

    static PrepareContext create(HepPlanner planner) {
      return new PrepareContext(planner, castNonNull(null), castNonNull(null));
    }

    PrepareContext withProgramState(HepProgram.State programState) {
      return new PrepareContext(planner, programState, endGroupState);
    }

    PrepareContext withEndGroupState(EndGroup.State endGroupState) {
      return new PrepareContext(planner, programState, endGroupState);
    }
  }
}