blob: 801f9742b90e7dea3eb4b64695d1dc2e09687790 [file] [log] [blame]
/*
* 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.rel.convert;
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelRule;
import org.apache.calcite.plan.RelTrait;
import org.apache.calcite.plan.RelTraitDef;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.tools.RelBuilderFactory;
import org.apache.calcite.util.ImmutableBeans;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.immutables.value.Value;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
import static org.apache.calcite.linq4j.Nullness.castNonNull;
/**
* Abstract base class for a rule which converts from one calling convention to
* another without changing semantics.
*/
@Value.Enclosing
public abstract class ConverterRule
extends RelRule<ConverterRule.Config> {
//~ Instance fields --------------------------------------------------------
private final RelTrait inTrait;
private final RelTrait outTrait;
protected final Convention out;
//~ Constructors -----------------------------------------------------------
/** Creates a <code>ConverterRule</code>. */
protected ConverterRule(Config config) {
super(config);
this.inTrait = Objects.requireNonNull(config.inTrait());
this.outTrait = Objects.requireNonNull(config.outTrait());
// Source and target traits must have same type
assert inTrait.getTraitDef() == outTrait.getTraitDef();
// Most sub-classes are concerned with converting one convention to
// another, and for them, the "out" field is a convenient short-cut.
this.out = outTrait instanceof Convention ? (Convention) outTrait
: castNonNull(null);
}
/**
* Creates a <code>ConverterRule</code>.
*
* @param clazz Type of relational expression to consider converting
* @param in Trait of relational expression to consider converting
* @param out Trait which is converted to
* @param descriptionPrefix Description prefix of rule
*
* @deprecated Use {@link #ConverterRule(Config)}
*/
@Deprecated // to be removed before 2.0
protected ConverterRule(Class<? extends RelNode> clazz, RelTrait in,
RelTrait out, String descriptionPrefix) {
this(Config.INSTANCE
.withConversion(clazz, in, out, descriptionPrefix));
}
@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
protected <R extends RelNode> ConverterRule(Class<R> clazz,
com.google.common.base.Predicate<? super R> predicate,
RelTrait in, RelTrait out, String descriptionPrefix) {
this(Config.INSTANCE
.withConversion(clazz, (Predicate<? super R>) predicate::apply,
in, out, descriptionPrefix));
}
/**
* Creates a <code>ConverterRule</code> with a predicate.
*
* @param clazz Type of relational expression to consider converting
* @param predicate Predicate on the relational expression
* @param in Trait of relational expression to consider converting
* @param out Trait which is converted to
* @param relBuilderFactory Builder for relational expressions
* @param descriptionPrefix Description prefix of rule
*
* @deprecated Use {@link #ConverterRule(Config)}
*/
@Deprecated // to be removed before 2.0
protected <R extends RelNode> ConverterRule(Class<R> clazz,
Predicate<? super R> predicate, RelTrait in, RelTrait out,
RelBuilderFactory relBuilderFactory, String descriptionPrefix) {
this(ImmutableConverterRule.Config.builder()
.withRelBuilderFactory(relBuilderFactory)
.build()
.withConversion(clazz, predicate, in, out, descriptionPrefix));
}
@SuppressWarnings("Guava")
@Deprecated // to be removed before 2.0
protected <R extends RelNode> ConverterRule(Class<R> clazz,
com.google.common.base.Predicate<? super R> predicate, RelTrait in,
RelTrait out, RelBuilderFactory relBuilderFactory, String description) {
this(clazz, (Predicate<? super R>) predicate::apply, in, out,
relBuilderFactory, description);
}
//~ Methods ----------------------------------------------------------------
@Override public Convention getOutConvention() {
return (Convention) outTrait;
}
@Override public RelTrait getOutTrait() {
return outTrait;
}
public RelTrait getInTrait() {
return inTrait;
}
public RelTraitDef getTraitDef() {
return inTrait.getTraitDef();
}
private static String createDescription(String descriptionPrefix,
RelTrait in, RelTrait out) {
return String.format(Locale.ROOT, "%s(in:%s,out:%s)",
Objects.toString(descriptionPrefix, "ConverterRule"), in, out);
}
/** Converts a relational expression to the target trait(s) of this rule.
*
* <p>Returns null if conversion is not possible. */
public abstract @Nullable RelNode convert(RelNode rel);
/**
* Returns true if this rule can convert <em>any</em> relational expression
* of the input convention.
*
* <p>The union-to-java converter, for example, is not guaranteed, because
* it only works on unions.</p>
*
* @return {@code true} if this rule can convert <em>any</em> relational
* expression
*/
public boolean isGuaranteed() {
return false;
}
@Override public void onMatch(RelOptRuleCall call) {
RelNode rel = call.rel(0);
if (rel.getTraitSet().contains(inTrait)) {
final RelNode converted = convert(rel);
if (converted != null) {
call.transformTo(converted);
}
}
}
//~ Inner Classes ----------------------------------------------------------
/** Rule configuration. */
@Value.Immutable(singleton = false)
public interface Config extends RelRule.Config {
Config INSTANCE = ImmutableConverterRule.Config.builder()
.withInTrait(Convention.NONE)
.withOutTrait(Convention.NONE)
.withRuleFactory(new Function<Config, ConverterRule>() {
@Override public ConverterRule apply(final Config config) {
throw new UnsupportedOperationException("A rule factory must be provided");
}
}).build();
@ImmutableBeans.Property
RelTrait inTrait();
/** Sets {@link #inTrait}. */
Config withInTrait(RelTrait trait);
@ImmutableBeans.Property
RelTrait outTrait();
/** Sets {@link #outTrait}. */
Config withOutTrait(RelTrait trait);
@ImmutableBeans.Property
Function<Config, ConverterRule> ruleFactory();
/** Sets {@link #outTrait}. */
Config withRuleFactory(Function<Config, ConverterRule> factory);
default <R extends RelNode> Config withConversion(Class<R> clazz,
Predicate<? super R> predicate, RelTrait in, RelTrait out,
String descriptionPrefix) {
return withInTrait(in)
.withOutTrait(out)
.withOperandSupplier(b ->
b.operand(clazz).predicate(predicate).convert(in))
.withDescription(createDescription(descriptionPrefix, in, out))
.as(Config.class);
}
default Config withConversion(Class<? extends RelNode> clazz, RelTrait in,
RelTrait out, String descriptionPrefix) {
return withConversion(clazz, r -> true, in, out, descriptionPrefix);
}
@Override default RelOptRule toRule() {
return toRule(ConverterRule.class);
}
default <R extends ConverterRule> R toRule(Class<R> ruleClass) {
return ruleClass.cast(ruleFactory().apply(this));
}
}
}