| /* |
| * 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; |
| |
| import org.apache.calcite.plan.Convention; |
| import org.apache.calcite.plan.DeriveMode; |
| import org.apache.calcite.plan.RelOptRule; |
| import org.apache.calcite.plan.RelTraitSet; |
| import org.apache.calcite.rel.core.Sort; |
| import org.apache.calcite.util.Pair; |
| |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Physical node in a planner that is capable of doing |
| * physical trait propagation and derivation. |
| * |
| * <p>How to use? |
| * |
| * <ol> |
| * <li>Enable top-down optimization by setting |
| * {@link org.apache.calcite.plan.volcano.VolcanoPlanner#setTopDownOpt(boolean)}. |
| * </li> |
| * |
| * <li>Let your convention's rel interface extends {@link PhysicalNode}, |
| * see {@link org.apache.calcite.adapter.enumerable.EnumerableRel} as |
| * an example.</li> |
| * |
| * <li>Each physical operator overrides any one of the two methods: |
| * {@link PhysicalNode#passThrough(RelTraitSet)} or |
| * {@link PhysicalNode#passThroughTraits(RelTraitSet)} depending on |
| * your needs.</li> |
| * |
| * <li>Choose derive mode for each physical operator by overriding |
| * {@link PhysicalNode#getDeriveMode()}.</li> |
| * |
| * <li>If the derive mode is {@link DeriveMode#OMAKASE}, override |
| * method {@link PhysicalNode#derive(List)} in the physical operator, |
| * otherwise, override {@link PhysicalNode#derive(RelTraitSet, int)} |
| * or {@link PhysicalNode#deriveTraits(RelTraitSet, int)}.</li> |
| * |
| * <li>Mark your enforcer operator by overriding {@link RelNode#isEnforcer()}, |
| * see {@link Sort#isEnforcer()} as an example. This is important, |
| * because it can help {@code VolcanoPlanner} avoid unnecessary |
| * trait propagation and derivation, therefore improve optimization |
| * efficiency.</li> |
| * |
| * <li>Implement {@link Convention#enforce(RelNode, RelTraitSet)} |
| * in your convention, which generates appropriate physical enforcer. |
| * See {@link org.apache.calcite.adapter.enumerable.EnumerableConvention} |
| * as example. Simply return {@code null} if you don't want physical |
| * trait enforcement.</li> |
| * </ol> |
| */ |
| public interface PhysicalNode extends RelNode { |
| |
| /** |
| * Pass required traitset from parent node to child nodes, |
| * returns new node after traits is passed down. |
| */ |
| default @Nullable RelNode passThrough(RelTraitSet required) { |
| Pair<RelTraitSet, List<RelTraitSet>> p = passThroughTraits(required); |
| if (p == null) { |
| return null; |
| } |
| int size = getInputs().size(); |
| assert size == p.right.size(); |
| List<RelNode> list = new ArrayList<>(size); |
| for (int i = 0; i < size; i++) { |
| RelNode n = RelOptRule.convert(getInput(i), p.right.get(i)); |
| list.add(n); |
| } |
| return copy(p.left, list); |
| } |
| |
| /** |
| * Pass required traitset from parent node to child nodes, |
| * returns a pair of traits after traits is passed down. |
| * |
| * <p>Pair.left: the new traitset; |
| * Pair.right: the list of required traitsets for child nodes. |
| */ |
| default @Nullable Pair<RelTraitSet, List<RelTraitSet>> passThroughTraits( |
| RelTraitSet required) { |
| throw new RuntimeException(getClass().getName() |
| + "#passThroughTraits() is not implemented."); |
| } |
| |
| /** |
| * Derive traitset from child node, returns new node after |
| * traits derivation. |
| */ |
| default @Nullable RelNode derive(RelTraitSet childTraits, int childId) { |
| Pair<RelTraitSet, List<RelTraitSet>> p = deriveTraits(childTraits, childId); |
| if (p == null) { |
| return null; |
| } |
| int size = getInputs().size(); |
| assert size == p.right.size(); |
| List<RelNode> list = new ArrayList<>(size); |
| for (int i = 0; i < size; i++) { |
| RelNode node = getInput(i); |
| node = RelOptRule.convert(node, p.right.get(i)); |
| list.add(node); |
| } |
| return copy(p.left, list); |
| } |
| |
| /** |
| * Derive traitset from child node, returns a pair of traits after |
| * traits derivation. |
| * |
| * <p>Pair.left: the new traitset; |
| * Pair.right: the list of required traitsets for child nodes. |
| */ |
| default @Nullable Pair<RelTraitSet, List<RelTraitSet>> deriveTraits( |
| RelTraitSet childTraits, int childId) { |
| throw new RuntimeException(getClass().getName() |
| + "#deriveTraits() is not implemented."); |
| } |
| |
| /** |
| * Given a list of child traitsets, |
| * inputTraits.size() == getInput().size(), |
| * returns node list after traits derivation. This method is called |
| * ONLY when the derive mode is OMAKASE. |
| */ |
| default List<RelNode> derive(List<List<RelTraitSet>> inputTraits) { |
| throw new RuntimeException(getClass().getName() |
| + "#derive() is not implemented."); |
| } |
| |
| /** |
| * Returns mode of derivation. |
| */ |
| default DeriveMode getDeriveMode() { |
| return DeriveMode.LEFT_FIRST; |
| } |
| } |