com.hazelcast.org.apache.calcite.rel.rules.IntersectToDistinctRule Maven / Gradle / Ivy
/*
* 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.rel.rules;
import com.hazelcast.org.apache.calcite.plan.RelOptCluster;
import com.hazelcast.org.apache.calcite.plan.RelOptRuleCall;
import com.hazelcast.org.apache.calcite.plan.RelRule;
import com.hazelcast.org.apache.calcite.rel.RelNode;
import com.hazelcast.org.apache.calcite.rel.core.Intersect;
import com.hazelcast.org.apache.calcite.rel.logical.LogicalIntersect;
import com.hazelcast.org.apache.calcite.rex.RexBuilder;
import com.hazelcast.org.apache.calcite.tools.RelBuilder;
import com.hazelcast.org.apache.calcite.tools.RelBuilderFactory;
import com.hazelcast.org.apache.calcite.util.ImmutableBitSet;
import com.hazelcast.org.apache.calcite.util.Util;
import org.immutables.value.Value;
import java.math.BigDecimal;
/**
* Planner rule that translates a distinct
* {@link com.hazelcast.org.apache.calcite.rel.core.Intersect}
* (all
= false
)
* into a group of operators composed of
* {@link com.hazelcast.org.apache.calcite.rel.core.Union},
* {@link com.hazelcast.org.apache.calcite.rel.core.Aggregate}, etc.
*
* Rewrite: (GB-Union All-GB)-GB-UDTF (on all attributes)
*
*
Example
*
* Query: R1 Intersect All R2
*
*
R3 = GB(R1 on all attributes, count(*) as c)
* union all
* GB(R2 on all attributes, count(*) as c)
*
*
R4 = GB(R3 on all attributes, count(c) as cnt, min(c) as m)
*
*
Note that we do not need min(c)
in intersect distinct.
*
*
R5 = Filter(cnt == #branch)
*
*
If it is intersect all then
*
*
R6 = UDTF (R5) which will explode the tuples based on min(c)
* R7 = Project(R6 on all attributes)
*
*
Else
*
*
R6 = Proj(R5 on all attributes)
*
* @see com.hazelcast.org.apache.calcite.rel.rules.UnionToDistinctRule
* @see CoreRules#INTERSECT_TO_DISTINCT
*/
@Value.Enclosing
public class IntersectToDistinctRule
extends RelRule
implements TransformationRule {
/** Creates an IntersectToDistinctRule. */
protected IntersectToDistinctRule(Config config) {
super(config);
}
@Deprecated // to be removed before 2.0
public IntersectToDistinctRule(Class extends Intersect> intersectClass,
RelBuilderFactory relBuilderFactory) {
this(Config.DEFAULT.withRelBuilderFactory(relBuilderFactory)
.as(Config.class)
.withOperandFor(intersectClass));
}
//~ Methods ----------------------------------------------------------------
@Override public void onMatch(RelOptRuleCall call) {
final Intersect intersect = call.rel(0);
if (intersect.all) {
return; // nothing we can do
}
final RelOptCluster cluster = intersect.getCluster();
final RexBuilder rexBuilder = cluster.getRexBuilder();
final RelBuilder relBuilder = call.builder();
// 1st level GB: create a GB (col0, col1, count() as c) for each branch
for (RelNode input : intersect.getInputs()) {
relBuilder.push(input);
relBuilder.aggregate(relBuilder.groupKey(relBuilder.fields()),
relBuilder.countStar(null));
}
// create a union above all the branches
final int branchCount = intersect.getInputs().size();
relBuilder.union(true, branchCount);
final RelNode union = relBuilder.peek();
// 2nd level GB: create a GB (col0, col1, count(c)) for each branch
// the index of c is union.getRowType().getFieldList().size() - 1
final int fieldCount = union.getRowType().getFieldCount();
final ImmutableBitSet groupSet =
ImmutableBitSet.range(fieldCount - 1);
relBuilder.aggregate(relBuilder.groupKey(groupSet),
relBuilder.countStar(null));
// add a filter count(c) = #branches
relBuilder.filter(
relBuilder.equals(relBuilder.field(fieldCount - 1),
rexBuilder.makeBigintLiteral(new BigDecimal(branchCount))));
// Project all but the last field
relBuilder.project(Util.skipLast(relBuilder.fields()));
// the schema for intersect distinct is like this
// R3 on all attributes + count(c) as cnt
// finally add a project to project out the last column
call.transformTo(relBuilder.build());
}
/** Rule configuration. */
@Value.Immutable
public interface Config extends RelRule.Config {
Config DEFAULT = ImmutableIntersectToDistinctRule.Config.of()
.withOperandFor(LogicalIntersect.class);
@Override default IntersectToDistinctRule toRule() {
return new IntersectToDistinctRule(this);
}
/** Defines an operand tree for the given classes. */
default Config withOperandFor(Class extends Intersect> intersectClass) {
return withOperandSupplier(b -> b.operand(intersectClass).anyInputs())
.as(Config.class);
}
}
}