Skip to content

Commit

Permalink
[CALCITE-3923] Refactor how planner rules are parameterized
Browse files Browse the repository at this point in the history
Create operands using a builder interface, OperandBuilder, and
deprecate methods RelOptRule.operand etc.

The change is backwards compatible, in the sense that
existing rule constructors are deprecated but still work.
From now on, to create rules, call RelOptRule.Config.toRule()
(which calls the rule's (Config) constructor).

Sub-classes of ConverterRule are a little different.  They
don't need their own sub-class of Config.  You just need to
call Config.withRuleFactory to specify the constructor of the
sub-class of ConverterRule.

Move rule instances into holder classes such as CoreRules,
MaterializedViewRules. Deprecate existing rule INSTANCE fields
(to be removed in 1.25). Deprecate previous rule constructors
(to be removed in 2.0).

Describe how to write rules in howto and tutorial.

Remove rule instances marked 'deprecated, to be removed before
1.25'.

Close apache#2024
  • Loading branch information
julianhyde committed Jul 28, 2020
1 parent b7aad0b commit 998cd83
Show file tree
Hide file tree
Showing 198 changed files with 9,018 additions and 5,503 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.RelOptRule;
import org.apache.calcite.plan.RelOptRuleCall;
import org.apache.calcite.plan.RelOptRuleOperand;
import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelRule;
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.RelCollations;
import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.convert.ConverterRule;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.logical.LogicalFilter;
import org.apache.calcite.rel.logical.LogicalProject;
Expand All @@ -54,18 +53,29 @@
* calling convention.
*/
public class CassandraRules {

private CassandraRules() {}

public static final CassandraFilterRule FILTER =
CassandraFilterRule.Config.DEFAULT.toRule();
public static final CassandraProjectRule PROJECT =
CassandraProjectRule.DEFAULT_CONFIG.toRule(CassandraProjectRule.class);
public static final CassandraSortRule SORT =
CassandraSortRule.Config.DEFAULT.toRule();
public static final CassandraLimitRule LIMIT =
CassandraLimitRule.Config.DEFAULT.toRule();

/** Rule to convert a relational expression from
* {@link CassandraRel#CONVENTION} to {@link EnumerableConvention}. */
public static final ConverterRule TO_ENUMERABLE =
new CassandraToEnumerableConverterRule(RelFactories.LOGICAL_BUILDER);
public static final CassandraToEnumerableConverterRule TO_ENUMERABLE =
CassandraToEnumerableConverterRule.DEFAULT_CONFIG
.toRule(CassandraToEnumerableConverterRule.class);

public static final RelOptRule[] RULES = {
CassandraFilterRule.INSTANCE,
CassandraProjectRule.INSTANCE,
CassandraSortRule.INSTANCE,
CassandraLimitRule.INSTANCE
FILTER,
PROJECT,
SORT,
LIMIT
};

static List<String> cassandraFieldNames(final RelDataType rowType) {
Expand Down Expand Up @@ -94,39 +104,29 @@ protected RexToCassandraTranslator(JavaTypeFactory typeFactory,
/** Base class for planner rules that convert a relational expression to
* Cassandra calling convention. */
abstract static class CassandraConverterRule extends ConverterRule {
protected final Convention out;

CassandraConverterRule(Class<? extends RelNode> clazz,
String description) {
this(clazz, r -> true, description);
}

<R extends RelNode> CassandraConverterRule(Class<R> clazz,
Predicate<? super R> predicate,
String description) {
super(clazz, predicate, Convention.NONE,
CassandraRel.CONVENTION, RelFactories.LOGICAL_BUILDER, description);
this.out = CassandraRel.CONVENTION;
CassandraConverterRule(Config config) {
super(config);
}
}

/**
* Rule to convert a {@link org.apache.calcite.rel.logical.LogicalFilter} to a
* {@link CassandraFilter}.
*
* @see #FILTER
*/
private static class CassandraFilterRule extends RelOptRule {
public static class CassandraFilterRule
extends RelRule<CassandraFilterRule.Config> {
/** Creates a CassandraFilterRule. */
protected CassandraFilterRule(Config config) {
super(config);
}

private static final Predicate<LogicalFilter> PREDICATE =
// TODO: Check for an equality predicate on the partition key
// Right now this just checks if we have a single top-level AND
filter -> RelOptUtil.disjunctions(filter.getCondition()).size() == 1;

private static final CassandraFilterRule INSTANCE = new CassandraFilterRule();

private CassandraFilterRule() {
super(operand(LogicalFilter.class, operand(CassandraTableScan.class, none())),
"CassandraFilterRule");
}

@Override public boolean matches(RelOptRuleCall call) {
// Get the condition from the filter operation
LogicalFilter filter = call.rel(0);
Expand Down Expand Up @@ -206,7 +206,7 @@ private String compareFieldWithLiteral(RexNode left, RexNode right, List<String>
}

/** @see org.apache.calcite.rel.convert.ConverterRule */
public void onMatch(RelOptRuleCall call) {
@Override public void onMatch(RelOptRuleCall call) {
LogicalFilter filter = call.rel(0);
CassandraTableScan scan = call.rel(1);
if (filter.getTraitSet().contains(Convention.NONE)) {
Expand All @@ -217,7 +217,7 @@ public void onMatch(RelOptRuleCall call) {
}
}

public RelNode convert(LogicalFilter filter, CassandraTableScan scan) {
RelNode convert(LogicalFilter filter, CassandraTableScan scan) {
final RelTraitSet traitSet = filter.getTraitSet().replace(CassandraRel.CONVENTION);
final Pair<List<String>, List<String>> keyFields = scan.cassandraTable.getKeyFields();
return new CassandraFilter(
Expand All @@ -229,17 +229,37 @@ public RelNode convert(LogicalFilter filter, CassandraTableScan scan) {
keyFields.right,
scan.cassandraTable.getClusteringOrder());
}

/** Rule configuration. */
public interface Config extends RelRule.Config {
Config DEFAULT = EMPTY
.withOperandSupplier(b0 ->
b0.operand(LogicalFilter.class)
.oneInput(b1 -> b1.operand(CassandraTableScan.class)
.noInputs()))
.as(Config.class);

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

/**
* Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject}
* to a {@link CassandraProject}.
*
* @see #PROJECT
*/
private static class CassandraProjectRule extends CassandraConverterRule {
private static final CassandraProjectRule INSTANCE = new CassandraProjectRule();

private CassandraProjectRule() {
super(LogicalProject.class, "CassandraProjectRule");
public static class CassandraProjectRule extends CassandraConverterRule {
/** Default configuration. */
private static final Config DEFAULT_CONFIG = Config.INSTANCE
.withConversion(LogicalProject.class, Convention.NONE,
CassandraRel.CONVENTION, "CassandraProjectRule")
.withRuleFactory(CassandraProjectRule::new);

protected CassandraProjectRule(Config config) {
super(config);
}

@Override public boolean matches(RelOptRuleCall call) {
Expand All @@ -253,7 +273,7 @@ private CassandraProjectRule() {
return true;
}

public RelNode convert(RelNode rel) {
@Override public RelNode convert(RelNode rel) {
final LogicalProject project = (LogicalProject) rel;
final RelTraitSet traitSet = project.getTraitSet().replace(out);
return new CassandraProject(project.getCluster(), traitSet,
Expand All @@ -265,23 +285,14 @@ public RelNode convert(RelNode rel) {
/**
* Rule to convert a {@link org.apache.calcite.rel.core.Sort} to a
* {@link CassandraSort}.
*
* @see #SORT
*/
private static class CassandraSortRule extends RelOptRule {

private static final RelOptRuleOperand CASSANDRA_OP =
operand(CassandraToEnumerableConverter.class,
operandJ(CassandraFilter.class, null,
// We can only use implicit sorting within a single partition
CassandraFilter::isSinglePartition, any()));

private static final CassandraSortRule INSTANCE = new CassandraSortRule();

private CassandraSortRule() {
super(
operandJ(Sort.class, null,
// Limits are handled by CassandraLimit
sort -> sort.offset == null && sort.fetch == null, CASSANDRA_OP),
"CassandraSortRule");
public static class CassandraSortRule
extends RelRule<CassandraSortRule.Config> {
/** Creates a CassandraSortRule. */
protected CassandraSortRule(Config config) {
super(config);
}

public RelNode convert(Sort sort, CassandraFilter filter) {
Expand All @@ -293,7 +304,7 @@ public RelNode convert(Sort sort, CassandraFilter filter) {
sort.getCollation());
}

public boolean matches(RelOptRuleCall call) {
@Override public boolean matches(RelOptRuleCall call) {
final Sort sort = call.rel(0);
final CassandraFilter filter = call.rel(2);
return collationsCompatible(sort.getCollation(), filter.getImplicitCollation());
Expand Down Expand Up @@ -360,26 +371,52 @@ private RelFieldCollation.Direction reverseDirection(RelFieldCollation.Direction
}

/** @see org.apache.calcite.rel.convert.ConverterRule */
public void onMatch(RelOptRuleCall call) {
@Override public void onMatch(RelOptRuleCall call) {
final Sort sort = call.rel(0);
CassandraFilter filter = call.rel(2);
final RelNode converted = convert(sort, filter);
if (converted != null) {
call.transformTo(converted);
}
}

/** Rule configuration. */
public interface Config extends RelRule.Config {
Config DEFAULT = EMPTY
.withOperandSupplier(b0 ->
b0.operand(Sort.class)
// Limits are handled by CassandraLimit
.predicate(sort ->
sort.offset == null && sort.fetch == null)
.oneInput(b1 ->
b1.operand(CassandraToEnumerableConverter.class)
.oneInput(b2 ->
b2.operand(CassandraFilter.class)
// We can only use implicit sorting within a
// single partition
.predicate(
CassandraFilter::isSinglePartition)
.anyInputs())))
.as(Config.class);

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

/**
* Rule to convert a {@link org.apache.calcite.adapter.enumerable.EnumerableLimit} to a
* Rule to convert a
* {@link org.apache.calcite.adapter.enumerable.EnumerableLimit} to a
* {@link CassandraLimit}.
*
* @see #LIMIT
*/
private static class CassandraLimitRule extends RelOptRule {
private static final CassandraLimitRule INSTANCE = new CassandraLimitRule();

private CassandraLimitRule() {
super(operand(EnumerableLimit.class, operand(CassandraToEnumerableConverter.class, any())),
"CassandraLimitRule");
public static class CassandraLimitRule
extends RelRule<CassandraLimitRule.Config> {
/** Creates a CassandraLimitRule. */
protected CassandraLimitRule(Config config) {
super(config);
}

public RelNode convert(EnumerableLimit limit) {
Expand All @@ -390,12 +427,27 @@ public RelNode convert(EnumerableLimit limit) {
}

/** @see org.apache.calcite.rel.convert.ConverterRule */
public void onMatch(RelOptRuleCall call) {
@Override public void onMatch(RelOptRuleCall call) {
final EnumerableLimit limit = call.rel(0);
final RelNode converted = convert(limit);
if (converted != null) {
call.transformTo(converted);
}
}

/** Rule configuration. */
public interface Config extends RelRule.Config {
Config DEFAULT = EMPTY
.withOperandSupplier(b0 ->
b0.operand(EnumerableLimit.class)
.oneInput(b1 ->
b1.operand(CassandraToEnumerableConverter.class)
.anyInputs()))
.as(Config.class);

@Override default CassandraLimitRule toRule() {
return new CassandraLimitRule(this);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,23 @@
import org.apache.calcite.plan.RelTraitSet;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.convert.ConverterRule;
import org.apache.calcite.tools.RelBuilderFactory;

import java.util.function.Predicate;

/**
* Rule to convert a relational expression from
* {@link CassandraRel#CONVENTION} to {@link EnumerableConvention}.
*
* @see CassandraRules#TO_ENUMERABLE
*/
public class CassandraToEnumerableConverterRule extends ConverterRule {
/** @deprecated Use {@link CassandraRules#TO_ENUMERABLE}. */
@Deprecated // to be removed before 1.25
public static final ConverterRule INSTANCE =
CassandraRules.TO_ENUMERABLE;
/** Default configuration. */
public static final Config DEFAULT_CONFIG = Config.INSTANCE
.withConversion(RelNode.class, CassandraRel.CONVENTION,
EnumerableConvention.INSTANCE, "CassandraToEnumerableConverterRule")
.withRuleFactory(CassandraToEnumerableConverterRule::new);

/**
* Creates a CassandraToEnumerableConverterRule.
*
* @param relBuilderFactory Builder for relational expressions
*/
public CassandraToEnumerableConverterRule(
RelBuilderFactory relBuilderFactory) {
super(RelNode.class, (Predicate<RelNode>) r -> true,
CassandraRel.CONVENTION, EnumerableConvention.INSTANCE,
relBuilderFactory, "CassandraToEnumerableConverterRule");
/** Creates a CassandraToEnumerableConverterRule. */
protected CassandraToEnumerableConverterRule(Config config) {
super(config);
}

@Override public RelNode convert(RelNode rel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,22 @@
/**
* Rule to convert a {@link org.apache.calcite.rel.logical.LogicalAggregate}
* to an {@link EnumerableAggregate}.
*
* @see EnumerableRules#ENUMERABLE_AGGREGATE_RULE
*/
class EnumerableAggregateRule extends ConverterRule {
EnumerableAggregateRule() {
super(LogicalAggregate.class, Convention.NONE,
EnumerableConvention.INSTANCE, "EnumerableAggregateRule");
/** Default configuration. */
static final Config DEFAULT_CONFIG = Config.INSTANCE
.withConversion(LogicalAggregate.class, Convention.NONE,
EnumerableConvention.INSTANCE, "EnumerableAggregateRule")
.withRuleFactory(EnumerableAggregateRule::new);

/** Called from the Config. */
protected EnumerableAggregateRule(Config config) {
super(config);
}

public RelNode convert(RelNode rel) {
@Override public RelNode convert(RelNode rel) {
final LogicalAggregate agg = (LogicalAggregate) rel;
final RelTraitSet traitSet = rel.getCluster()
.traitSet().replace(EnumerableConvention.INSTANCE);
Expand Down
Loading

0 comments on commit 998cd83

Please sign in to comment.