PhysicalNode.java

/*
 * 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;
  }
}