| /* |
| * 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.Objects; |
| import java.util.function.BiConsumer; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| |
| /** |
| * 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> |
| * @Override default CsvProjectTableScanRule toRule() {<br> |
| * 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> |
| * /** Returns foo. */ |
| * int foo(); |
| * |
| * /** Sets {@link #foo}. */ |
| * 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 -> |
| * b0.operand(LogicalProject.class).oneInput(b1 -> |
| * 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> |
| * /** Rule that matches a {@code Project} on a |
| * * {@code CsvTableScan} and pushes down projects if possible. */ |
| * 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(); |
| final Done done = transform.apply(b); |
| Objects.requireNonNull(done, "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 = Objects.requireNonNull(parent, "parent"); |
| this.relClass = Objects.requireNonNull(relClass, "relClass"); |
| } |
| |
| @Override public OperandDetailBuilderImpl<R> trait(RelTrait trait) { |
| this.trait = Objects.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); |
| Objects.requireNonNull(done, "done"); |
| return done(RelOptRuleOperandChildPolicy.SOME); |
| } |
| |
| @Override public Done inputs(OperandTransform... transforms) { |
| for (OperandTransform transform : transforms) { |
| final Done done = transform.apply(inputBuilder); |
| Objects.requireNonNull(done, "done"); |
| } |
| return done(RelOptRuleOperandChildPolicy.SOME); |
| } |
| |
| @Override public Done unorderedInputs(OperandTransform... transforms) { |
| for (OperandTransform transform : transforms) { |
| final Done done = transform.apply(inputBuilder); |
| Objects.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> { |
| } |
| } |