RelRule.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;

import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.tools.RelBuilderFactory;

import com.google.common.collect.ImmutableList;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.immutables.value.Value;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.util.Objects.requireNonNull;

/**
 * Rule that is parameterized via a configuration.
 *
 * <p>Eventually (before Calcite version 2.0), this class will replace
 * {@link RelOptRule}. Constructors of {@code RelOptRule} are deprecated, so new
 * rule classes should extend {@code RelRule}, not {@code RelOptRule}.
 * Next, we will deprecate {@code RelOptRule}, so that variables that reference
 * rules will be of type {@code RelRule}.
 *
 * <p><b>Guidelines for writing rules</b>
 *
 * <p>1. If your rule is a sub-class of
 * {@link org.apache.calcite.rel.convert.ConverterRule}
 * and does not need any extra properties,
 * there's no need to create an {@code interface Config} inside your class.
 * In your class, create a constant
 * {@code public static final Config DEFAULT_CONFIG}. Goto step 5.
 *
 * <p>2. If your rule is not a sub-class of
 * {@link org.apache.calcite.rel.convert.ConverterRule},
 * create an inner {@code interface Config extends RelRule.Config} and
 * annotate it with {@code @Value.Immutable}. Note, if your inner class
 * is two levels deep (e.g. top-level Rule with Config inside), we recommend
 * you annotate the outer class with {@code @Value.Enclosing} which will
 * instruct the annotation processor to put your generated value class
 * inside a new Immutable outer class. If your rule is three levels deep,
 * the best thing to do is give your class a unique name to avoid any
 * generated code class name overlaps.
 * Implement {@link Config#toRule() toRule} using a {@code default} method:
 *
 * <blockquote>
 * <code>
 * &#x40;Override default CsvProjectTableScanRule toRule() {<br>
 * &nbsp;&nbsp;return new CsvProjectTableScanRule(this);<br>
 * }
 * </code>
 * </blockquote>
 *
 * <p>3. For each configuration property, create a pair of methods in your
 * {@code Config} interface. For example, for a property {@code foo} of type
 * {@code int}, create methods {@code foo} and {@code withFoo}:
 *
 * <blockquote><pre><code>
 * &#x2f;** Returns foo. *&#x2f;
 * int foo();
 *
 * &#x2f;** Sets {&#x40;link #foo}. *&#x2f;
 * Config withFoo(int x);
 * </code></pre></blockquote>
 *
 * <p>4. In your {@code Config} interface, create a {@code DEFAULT} constant
 * that represents the most typical configuration of your rule. This default
 * will leverage the Immutables class generated by the Annotation Processor
 * based on the annotation you provided above. For example,
 * {@code CsvProjectTableScanRule.Config} has the following:
 *
 * <blockquote><pre><code>
 * Config DEFAULT = ImmutableCsvProjectTableScanRule.Config.builder()
 *     .withOperandSupplier(b0 -&gt;
 *         b0.operand(LogicalProject.class).oneInput(b1 -&gt;
 *             b1.operand(CsvTableScan.class).noInputs()))
 *      .build();
 * </code></pre></blockquote>
 *
 * <p>5. Do not create an {@code INSTANCE} constant inside your rule.
 * Instead, create a named instance of your rule, with default configuration,
 * in a holder class. The holder class must not be a sub-class of
 * {@code RelOptRule} (otherwise cyclic class-loading issues may arise).
 * Generally it will be called <code><i>Xxx</i>Rules</code>, for example
 * {@code CsvRules}. The rule instance is named after your rule, and is based
 * on the default config ({@code Config.DEFAULT}, or {@code DEFAULT_CONFIG} for
 * converter rules):
 *
 * <blockquote><pre><code>
 * &#x2f;** Rule that matches a {&#x40;code Project} on a
 *  * {&#x40;code CsvTableScan} and pushes down projects if possible. *&#x2f;
 * public static final CsvProjectTableScanRule PROJECT_SCAN =
 *     CsvProjectTableScanRule.Config.DEFAULT.toRule();
 * </code></pre></blockquote>
 *
 * @param <C> Configuration type
 */
public abstract class RelRule<C extends RelRule.Config> extends RelOptRule {
  public final C config;

  /** Creates a RelRule. */
  protected RelRule(C config) {
    super(OperandBuilderImpl.operand(config.operandSupplier()),
        config.relBuilderFactory(), config.description());
    this.config = config;
  }

  /** Rule configuration. */
  public interface Config {

    /** Creates a rule that uses this configuration. Sub-class must override. */
    RelOptRule toRule();

    /** Casts this configuration to another type, usually a sub-class. */
    default <T extends Object> T as(Class<T> class_) {
      if (class_.isAssignableFrom(this.getClass())) {
        return class_.cast(this);
      } else {
        throw new UnsupportedOperationException(
            String.format(Locale.ROOT,
                "The current config of type %s is not an instance of %s.",
                this.getClass(),
                class_));
      }
    }

    /** The factory that is used to create a
     * {@link org.apache.calcite.tools.RelBuilder} during rule invocations. */
    @Value.Default default RelBuilderFactory relBuilderFactory() {
      return RelFactories.LOGICAL_BUILDER;
    }

    /** Sets {@link #relBuilderFactory()}. */
    Config withRelBuilderFactory(RelBuilderFactory factory);

    /** Description of the rule instance. */
    // CALCITE-4831: remove the second nullable annotation once immutables/#1261 is fixed
    @javax.annotation.Nullable @Nullable String description();

    /** Sets {@link #description()}. */
    Config withDescription(@Nullable String description);

    /** Creates the operands for the rule instance. */
    @Value.Default default OperandTransform operandSupplier() {
      return s -> {
        throw new IllegalArgumentException("Rules must have at least one "
            + "operand. Call Config.withOperandSupplier to specify them.");
      };

    }

    /** Sets {@link #operandSupplier()}. */
    Config withOperandSupplier(OperandTransform transform);
  }

  /** Function that creates an operand.
   *
   * @see Config#withOperandSupplier(OperandTransform) */
  @FunctionalInterface
  public interface OperandTransform extends Function<OperandBuilder, Done> {
  }

  /** Callback to create an operand.
   *
   * @see OperandTransform */
  public interface OperandBuilder {
    /** Starts building an operand by specifying its class.
     * Call further methods on the returned {@link OperandDetailBuilder} to
     * complete the operand. */
    <R extends RelNode> OperandDetailBuilder<R> operand(Class<R> relClass);

    /** Supplies an operand that has been built manually. */
    Done exactly(RelOptRuleOperand operand);
  }

  /** Indicates that an operand is complete.
   *
   * @see OperandTransform */
  public interface Done {
  }

  /** Add details about an operand, such as its inputs.
   *
   * @param <R> Type of relational expression */
  public interface OperandDetailBuilder<R extends RelNode> {
    /** Sets a trait of this operand. */
    OperandDetailBuilder<R> trait(RelTrait trait);

    /** Sets the predicate of this operand. */
    OperandDetailBuilder<R> predicate(Predicate<? super R> predicate);

    /** Indicates that this operand has a single input. */
    Done oneInput(OperandTransform transform);

    /** Indicates that this operand has several inputs. */
    Done inputs(OperandTransform... transforms);

    /** Indicates that this operand has several inputs, unordered. */
    Done unorderedInputs(OperandTransform... transforms);

    /** Indicates that this operand takes any number or type of inputs. */
    Done anyInputs();

    /** Indicates that this operand takes no inputs. */
    Done noInputs();

    /** Indicates that this operand converts a relational expression to
     * another trait. */
    Done convert(RelTrait in);
  }

  /** Implementation of {@link OperandBuilder}. */
  private static class OperandBuilderImpl implements OperandBuilder {
    final List<RelOptRuleOperand> operands = new ArrayList<>();

    static RelOptRuleOperand operand(OperandTransform transform) {
      final OperandBuilderImpl b = new OperandBuilderImpl();
      requireNonNull(transform.apply(b), "done");
      if (b.operands.size() != 1) {
        throw new IllegalArgumentException("operand supplier must call one of "
            + "the following methods: operand or exactly");
      }
      return b.operands.get(0);
    }

    @Override public <R extends RelNode> OperandDetailBuilder<R> operand(Class<R> relClass) {
      return new OperandDetailBuilderImpl<>(this, relClass);
    }

    @Override public Done exactly(RelOptRuleOperand operand) {
      operands.add(operand);
      return DoneImpl.INSTANCE;
    }
  }

  /** Implementation of {@link OperandDetailBuilder}.
   *
   * @param <R> Type of relational expression */
  private static class OperandDetailBuilderImpl<R extends RelNode>
      implements OperandDetailBuilder<R> {
    private final OperandBuilderImpl parent;
    private final Class<R> relClass;
    final OperandBuilderImpl inputBuilder = new OperandBuilderImpl();
    private @Nullable RelTrait trait;
    private Predicate<? super R> predicate = r -> true;

    OperandDetailBuilderImpl(OperandBuilderImpl parent, Class<R> relClass) {
      this.parent = requireNonNull(parent, "parent");
      this.relClass = requireNonNull(relClass, "relClass");
    }

    @Override public OperandDetailBuilderImpl<R> trait(RelTrait trait) {
      this.trait = requireNonNull(trait, "trait");
      return this;
    }

    @Override public OperandDetailBuilderImpl<R> predicate(Predicate<? super R> predicate) {
      this.predicate = predicate;
      return this;
    }

    /** Indicates that there are no more inputs. */
    Done done(RelOptRuleOperandChildPolicy childPolicy) {
      parent.operands.add(
          new RelOptRuleOperand(relClass, trait, predicate, childPolicy,
              ImmutableList.copyOf(inputBuilder.operands)));
      return DoneImpl.INSTANCE;
    }

    @Override public Done convert(RelTrait in) {
      parent.operands.add(
          new ConverterRelOptRuleOperand(relClass, in, predicate));
      return DoneImpl.INSTANCE;
    }

    @Override public Done noInputs() {
      return done(RelOptRuleOperandChildPolicy.LEAF);
    }

    @Override public Done anyInputs() {
      return done(RelOptRuleOperandChildPolicy.ANY);
    }

    @Override public Done oneInput(OperandTransform transform) {
      final Done done = transform.apply(inputBuilder);
      requireNonNull(done, "done");
      return done(RelOptRuleOperandChildPolicy.SOME);
    }

    @Override public Done inputs(OperandTransform... transforms) {
      for (OperandTransform transform : transforms) {
        final Done done = transform.apply(inputBuilder);
        requireNonNull(done, "done");
      }
      return done(RelOptRuleOperandChildPolicy.SOME);
    }

    @Override public Done unorderedInputs(OperandTransform... transforms) {
      for (OperandTransform transform : transforms) {
        final Done done = transform.apply(inputBuilder);
        requireNonNull(done, "done");
      }
      return done(RelOptRuleOperandChildPolicy.UNORDERED);
    }
  }

  /** Singleton instance of {@link Done}. */
  private enum DoneImpl implements Done {
    INSTANCE
  }

  /** Callback interface that helps you avoid creating sub-classes of
   * {@link RelRule} that differ only in implementations of
   * {@link #onMatch(RelOptRuleCall)} method.
   *
   * @param <R> Rule type */
  public interface MatchHandler<R extends RelOptRule>
      extends BiConsumer<R, RelOptRuleCall> {
  }
}