com.hazelcast.org.apache.calcite.rel.rules.ProjectSetOpTransposeRule 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.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.Project;
import com.hazelcast.org.apache.calcite.rel.core.SetOp;
import com.hazelcast.org.apache.calcite.rel.logical.LogicalProject;
import com.hazelcast.org.apache.calcite.rex.RexInputRef;
import com.hazelcast.org.apache.calcite.rex.RexOver;
import com.hazelcast.org.apache.calcite.tools.RelBuilderFactory;
import org.immutables.value.Value;
import java.util.ArrayList;
import java.util.List;
/**
* Planner rule that pushes
* a {@link com.hazelcast.org.apache.calcite.rel.logical.LogicalProject}
* past a {@link com.hazelcast.org.apache.calcite.rel.core.SetOp}.
*
* The children of the {@code SetOp} will project
* only the {@link RexInputRef}s referenced in the original
* {@code LogicalProject}.
*
* @see CoreRules#PROJECT_SET_OP_TRANSPOSE
*/
@Value.Enclosing
public class ProjectSetOpTransposeRule
extends RelRule
implements TransformationRule {
/** Creates a ProjectSetOpTransposeRule. */
protected ProjectSetOpTransposeRule(Config config) {
super(config);
}
@Deprecated // to be removed before 2.0
public ProjectSetOpTransposeRule(
PushProjector.ExprCondition preserveExprCondition,
RelBuilderFactory relBuilderFactory) {
this(Config.DEFAULT.withRelBuilderFactory(relBuilderFactory)
.as(Config.class)
.withPreserveExprCondition(preserveExprCondition));
}
//~ Methods ----------------------------------------------------------------
@Override public void onMatch(RelOptRuleCall call) {
final LogicalProject origProject = call.rel(0);
final SetOp setOp = call.rel(1);
// cannot push project past a distinct
if (!setOp.all) {
return;
}
// locate all fields referenced in the projection
final PushProjector pushProjector =
new PushProjector(origProject, null, setOp,
config.preserveExprCondition(), call.builder());
pushProjector.locateAllRefs();
final List newSetOpInputs = new ArrayList<>();
final int[] adjustments = pushProjector.getAdjustments();
final RelNode node;
if (origProject.containsOver()) {
// should not push over past set-op but can push its operand down.
for (RelNode input : setOp.getInputs()) {
Project p = pushProjector.createProjectRefsAndExprs(input, true, false);
// make sure that it is not a trivial project to avoid infinite loop.
if (p.getRowType().equals(input.getRowType())) {
return;
}
newSetOpInputs.add(p);
}
final SetOp newSetOp =
setOp.copy(setOp.getTraitSet(), newSetOpInputs);
node = pushProjector.createNewProject(newSetOp, adjustments);
} else {
// push some expressions below the set-op; this
// is different from pushing below a join, where we decompose
// to try to keep expensive expressions above the join,
// because UNION ALL does not have any filtering effect,
// and it is the only operator this rule currently acts on
setOp.getInputs().forEach(input ->
newSetOpInputs.add(
pushProjector.createNewProject(
pushProjector.createProjectRefsAndExprs(
input, true, false), adjustments)));
node = setOp.copy(setOp.getTraitSet(), newSetOpInputs);
}
call.transformTo(node);
}
/** Rule configuration. */
@Value.Immutable(singleton = false)
public interface Config extends RelRule.Config {
Config DEFAULT = ImmutableProjectSetOpTransposeRule.Config.builder()
.withPreserveExprCondition(expr -> !(expr instanceof RexOver))
.build()
.withOperandSupplier(b0 ->
b0.operand(LogicalProject.class).oneInput(b1 ->
b1.operand(SetOp.class).anyInputs()));
@Override default ProjectSetOpTransposeRule toRule() {
return new ProjectSetOpTransposeRule(this);
}
/** Defines when an expression should not be pushed. */
PushProjector.ExprCondition preserveExprCondition();
/** Sets {@link #preserveExprCondition()}. */
Config withPreserveExprCondition(PushProjector.ExprCondition condition);
}
}