All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.plan.RelRule Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * 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 com.hazelcast.org.apache.calcite.plan;

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

import com.hazelcast.com.google.common.collect.ImmutableList;

import com.hazelcast.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.
 *
 * 

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}. * *

Guidelines for writing rules * *

1. If your rule is a sub-class of * {@link com.hazelcast.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. * *

2. If your rule is not a sub-class of * {@link com.hazelcast.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: * *

* * @Override default CsvProjectTableScanRule toRule() {
*   return new CsvProjectTableScanRule(this);
* } *
*
* *

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}: * *


 * /** Returns foo. */
 * int foo();
 *
 * /** Sets {@link #foo}. */
 * Config withFoo(int x);
 * 
* *

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: * *


 * Config DEFAULT = ImmutableCsvProjectTableScanRule.Config.builder()
 *     .withOperandSupplier(b0 ->
 *         b0.operand(LogicalProject.class).oneInput(b1 ->
 *             b1.operand(CsvTableScan.class).noInputs()))
 *      .build();
 * 
* *

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 XxxRules, 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): * *


 * /** 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();
 * 
* * @param Configuration type */ public abstract class RelRule 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 as(Class 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 com.hazelcast.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 { } /** 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. */ OperandDetailBuilder operand(Class 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 Type of relational expression */ public interface OperandDetailBuilder { /** Sets a trait of this operand. */ OperandDetailBuilder trait(RelTrait trait); /** Sets the predicate of this operand. */ OperandDetailBuilder predicate(Predicate 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 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 OperandDetailBuilder operand(Class relClass) { return new OperandDetailBuilderImpl<>(this, relClass); } @Override public Done exactly(RelOptRuleOperand operand) { operands.add(operand); return DoneImpl.INSTANCE; } } /** Implementation of {@link OperandDetailBuilder}. * * @param Type of relational expression */ private static class OperandDetailBuilderImpl implements OperandDetailBuilder { private final OperandBuilderImpl parent; private final Class relClass; final OperandBuilderImpl inputBuilder = new OperandBuilderImpl(); private @Nullable RelTrait trait; private Predicate predicate = r -> true; OperandDetailBuilderImpl(OperandBuilderImpl parent, Class relClass) { this.parent = Objects.requireNonNull(parent, "parent"); this.relClass = Objects.requireNonNull(relClass, "relClass"); } @Override public OperandDetailBuilderImpl trait(RelTrait trait) { this.trait = Objects.requireNonNull(trait, "trait"); return this; } @Override public OperandDetailBuilderImpl predicate(Predicate 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 Rule type */ public interface MatchHandler extends BiConsumer { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy