content: /*
   * 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.interpreter;
  
  import org.apache.calcite.DataContext;
  import org.apache.calcite.adapter.enumerable.AggImplementor;
  import org.apache.calcite.adapter.enumerable.RexImpTable;
  import org.apache.calcite.linq4j.Enumerable;
  import org.apache.calcite.plan.Convention;
  import org.apache.calcite.plan.RelOptCluster;
  import org.apache.calcite.plan.RelOptCost;
  import org.apache.calcite.plan.RelOptPlanner;
  import org.apache.calcite.plan.RelOptRule;
  import org.apache.calcite.plan.RelOptRuleCall;
  import org.apache.calcite.plan.RelOptTable;
  import org.apache.calcite.plan.RelRule;
  import org.apache.calcite.plan.RelTraitSet;
  import org.apache.calcite.rel.InvalidRelException;
  import org.apache.calcite.rel.RelCollation;
  import org.apache.calcite.rel.RelCollationTraitDef;
  import org.apache.calcite.rel.RelNode;
  import org.apache.calcite.rel.RelWriter;
  import org.apache.calcite.rel.convert.ConverterRule;
  import org.apache.calcite.rel.core.Aggregate;
  import org.apache.calcite.rel.core.AggregateCall;
  import org.apache.calcite.rel.core.CorrelationId;
  import org.apache.calcite.rel.core.Filter;
  import org.apache.calcite.rel.core.Intersect;
  import org.apache.calcite.rel.core.Join;
  import org.apache.calcite.rel.core.JoinRelType;
  import org.apache.calcite.rel.core.Match;
  import org.apache.calcite.rel.core.Minus;
  import org.apache.calcite.rel.core.Project;
  import org.apache.calcite.rel.core.SetOp;
  import org.apache.calcite.rel.core.Sort;
  import org.apache.calcite.rel.core.TableScan;
  import org.apache.calcite.rel.core.Union;
  import org.apache.calcite.rel.core.Values;
  import org.apache.calcite.rel.core.Window;
  import org.apache.calcite.rel.logical.LogicalAggregate;
  import org.apache.calcite.rel.logical.LogicalFilter;
  import org.apache.calcite.rel.logical.LogicalIntersect;
  import org.apache.calcite.rel.logical.LogicalJoin;
  import org.apache.calcite.rel.logical.LogicalMatch;
  import org.apache.calcite.rel.logical.LogicalProject;
  import org.apache.calcite.rel.logical.LogicalTableScan;
  import org.apache.calcite.rel.logical.LogicalUnion;
  import org.apache.calcite.rel.logical.LogicalValues;
  import org.apache.calcite.rel.logical.LogicalWindow;
  import org.apache.calcite.rel.metadata.RelMdCollation;
  import org.apache.calcite.rel.metadata.RelMetadataQuery;
  import org.apache.calcite.rel.type.RelDataType;
  import org.apache.calcite.rel.type.RelDataTypeFactory;
  import org.apache.calcite.rel.type.RelDataTypeField;
  import org.apache.calcite.rex.RexLiteral;
  import org.apache.calcite.rex.RexNode;
  import org.apache.calcite.schema.FilterableTable;
  import org.apache.calcite.schema.ProjectableFilterableTable;
  import org.apache.calcite.schema.ScannableTable;
  import org.apache.calcite.schema.Table;
  import org.apache.calcite.tools.RelBuilderFactory;
  import org.apache.calcite.util.ImmutableBitSet;
  import org.apache.calcite.util.ImmutableIntList;
  
  import com.google.common.collect.ImmutableList;
  import com.google.common.collect.ImmutableSet;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  import org.immutables.value.Value;
  
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.SortedSet;
  
  import static com.google.common.base.Preconditions.checkArgument;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Utilities pertaining to {@link BindableRel} and {@link BindableConvention}.
   */
  @Value.Enclosing
  public class Bindables {
    private Bindables() {}
  
    public static final RelOptRule BINDABLE_TABLE_SCAN_RULE =
        BindableTableScanRule.Config.DEFAULT.toRule();
  
    public static final RelOptRule BINDABLE_FILTER_RULE =
        BindableFilterRule.DEFAULT_CONFIG.toRule(BindableFilterRule.class);
  
    public static final RelOptRule BINDABLE_PROJECT_RULE =
        BindableProjectRule.DEFAULT_CONFIG.toRule(BindableProjectRule.class);
  
    public static final RelOptRule BINDABLE_SORT_RULE =
        BindableSortRule.DEFAULT_CONFIG.toRule(BindableSortRule.class);
  
    public static final RelOptRule BINDABLE_JOIN_RULE =
        BindableJoinRule.DEFAULT_CONFIG.toRule(BindableJoinRule.class);
  
    public static final RelOptRule BINDABLE_SET_OP_RULE =
        BindableSetOpRule.DEFAULT_CONFIG.toRule(BindableSetOpRule.class);
  
    // CHECKSTYLE: IGNORE 1
    /** @deprecated Use {@link #BINDABLE_SET_OP_RULE}. */
    @SuppressWarnings("MissingSummary")
    public static final RelOptRule BINDABLE_SETOP_RULE =
        BINDABLE_SET_OP_RULE;
  
    public static final RelOptRule BINDABLE_VALUES_RULE =
        BindableValuesRule.DEFAULT_CONFIG.toRule(BindableValuesRule.class);
  
    public static final RelOptRule BINDABLE_AGGREGATE_RULE =
        BindableAggregateRule.DEFAULT_CONFIG.toRule(BindableAggregateRule.class);
  
    public static final RelOptRule BINDABLE_WINDOW_RULE =
        BindableWindowRule.DEFAULT_CONFIG.toRule(BindableWindowRule.class);
  
    public static final RelOptRule BINDABLE_MATCH_RULE =
        BindableMatchRule.DEFAULT_CONFIG.toRule(BindableMatchRule.class);
  
    /** Rule that converts a relational expression from
     * {@link org.apache.calcite.plan.Convention#NONE}
     * to {@link org.apache.calcite.interpreter.BindableConvention}. */
    public static final NoneToBindableConverterRule FROM_NONE_RULE =
        NoneToBindableConverterRule.DEFAULT_CONFIG
            .toRule(NoneToBindableConverterRule.class);
  
    /** All rules that convert logical relational expression to bindable. */
    public static final ImmutableList<RelOptRule> RULES =
        ImmutableList.of(FROM_NONE_RULE,
            BINDABLE_TABLE_SCAN_RULE,
            BINDABLE_FILTER_RULE,
            BINDABLE_PROJECT_RULE,
            BINDABLE_SORT_RULE,
            BINDABLE_JOIN_RULE,
            BINDABLE_SET_OP_RULE,
            BINDABLE_VALUES_RULE,
            BINDABLE_AGGREGATE_RULE,
            BINDABLE_WINDOW_RULE,
            BINDABLE_MATCH_RULE);
  
    /** Helper method that converts a bindable relational expression into a
     * record iterator.
     *
     * <p>Any bindable can be compiled; if its input is also bindable, it becomes
     * part of the same compilation unit.
     */
    private static Enumerable<@Nullable Object[]> help(DataContext dataContext,
        BindableRel rel) {
      return new Interpreter(dataContext, rel);
    }
  
    /** Rule that converts a {@link org.apache.calcite.rel.core.TableScan}
     * to bindable convention.
     *
     * @see #BINDABLE_TABLE_SCAN_RULE */
    public static class BindableTableScanRule
        extends RelRule<BindableTableScanRule.Config> {
      /** Called from Config. */
      protected BindableTableScanRule(Config config) {
        super(config);
      }
  
      @Deprecated // to be removed before 2.0
      public BindableTableScanRule(RelBuilderFactory relBuilderFactory) {
        this(Config.DEFAULT.withRelBuilderFactory(relBuilderFactory)
            .as(Config.class));
      }
  
      @Override public void onMatch(RelOptRuleCall call) {
        final LogicalTableScan scan = call.rel(0);
        final RelOptTable table = scan.getTable();
        if (BindableTableScan.canHandle(table)) {
          call.transformTo(
              BindableTableScan.create(scan.getCluster(), table));
        }
      }
  
      /** Rule configuration. */
      @Value.Immutable
      public interface Config extends RelRule.Config {
        Config DEFAULT = ImmutableBindables.Config.of()
            .withOperandSupplier(b ->
                b.operand(LogicalTableScan.class).noInputs());
  
        @Override default BindableTableScanRule toRule() {
          return new BindableTableScanRule(this);
        }
      }
    }
  
    /** Scan of a table that implements {@link ScannableTable} and therefore can
     * be converted into an {@link Enumerable}. */
    public static class BindableTableScan
        extends TableScan implements BindableRel {
      public final ImmutableList<RexNode> filters;
      public final ImmutableIntList projects;
  
      /** Creates a BindableTableScan.
       *
       * <p>Use {@link #create} unless you know what you are doing. */
      BindableTableScan(RelOptCluster cluster, RelTraitSet traitSet,
          RelOptTable table, ImmutableList<RexNode> filters,
          ImmutableIntList projects) {
        super(cluster, traitSet, ImmutableList.of(), table);
        this.filters = requireNonNull(filters, "filters");
        this.projects = requireNonNull(projects, "projects");
        checkArgument(canHandle(table));
      }
  
      /** Creates a BindableTableScan. */
      public static BindableTableScan create(RelOptCluster cluster,
          RelOptTable relOptTable) {
        return create(cluster, relOptTable, ImmutableList.of(),
            identity(relOptTable));
      }
  
      /** Creates a BindableTableScan. */
      public static BindableTableScan create(RelOptCluster cluster,
          RelOptTable relOptTable, List<RexNode> filters,
          List<Integer> projects) {
        final Table table = relOptTable.unwrap(Table.class);
        final RelTraitSet traitSet =
            cluster.traitSetOf(BindableConvention.INSTANCE)
                .replaceIfs(RelCollationTraitDef.INSTANCE, () -> {
                  if (table != null) {
                    return table.getStatistic().getCollations();
                  }
                  return ImmutableList.of();
                });
        return new BindableTableScan(cluster, traitSet, relOptTable,
            ImmutableList.copyOf(filters), ImmutableIntList.copyOf(projects));
      }
  
      @Override public RelDataType deriveRowType() {
        final RelDataTypeFactory.Builder builder =
            getCluster().getTypeFactory().builder();
        final List<RelDataTypeField> fieldList =
            table.getRowType().getFieldList();
        for (int project : projects) {
          builder.add(fieldList.get(project));
        }
        return builder.build();
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public RelWriter explainTerms(RelWriter pw) {
        return super.explainTerms(pw)
            .itemIf("filters", filters, !filters.isEmpty())
            .itemIf("projects", projects, !projects.equals(identity()));
      }
  
      @Override public double estimateRowCount(RelMetadataQuery mq) {
        double f = filters.isEmpty() ? 1d : 0.5d;
        return super.estimateRowCount(mq) * f;
      }
  
      @Override public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner,
          RelMetadataQuery mq) {
        boolean noPushing = filters.isEmpty()
                && projects.size() == table.getRowType().getFieldCount();
        RelOptCost cost = super.computeSelfCost(planner, mq);
        if (noPushing || cost == null) {
          return cost;
        }
        // Cost factor for pushing filters
        double f = filters.isEmpty() ? 1d : 0.5d;
  
        // Cost factor for pushing fields
        // The "+ 2d" on top and bottom keeps the function fairly smooth.
        double p = ((double) projects.size() + 2d)
            / ((double) table.getRowType().getFieldCount() + 2d);
  
        // Multiply the cost by a factor that makes a scan more attractive if
        // filters and projects are pushed to the table scan
        return cost.multiplyBy(f * p * 0.01d);
      }
  
      public static boolean canHandle(RelOptTable table) {
        return table.maybeUnwrap(ScannableTable.class).isPresent()
            || table.maybeUnwrap(FilterableTable.class).isPresent()
            || table.maybeUnwrap(ProjectableFilterableTable.class).isPresent();
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        throw new UnsupportedOperationException(); // TODO:
      }
    }
  
    /** Rule that converts a {@link Filter} to bindable convention.
     *
     * @see #BINDABLE_FILTER_RULE */
    public static class BindableFilterRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalFilter.class, f -> !f.containsOver(),
              Convention.NONE, BindableConvention.INSTANCE,
              "BindableFilterRule")
          .withRuleFactory(BindableFilterRule::new);
  
      /** Called from the Config. */
      protected BindableFilterRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        final LogicalFilter filter = (LogicalFilter) rel;
        return BindableFilter.create(
            convert(filter.getInput(),
                filter.getInput().getTraitSet()
                    .replace(BindableConvention.INSTANCE)),
            filter.getCondition());
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Filter}
     * in bindable convention. */
    public static class BindableFilter extends Filter implements BindableRel {
      public BindableFilter(RelOptCluster cluster, RelTraitSet traitSet,
          RelNode input, RexNode condition) {
        super(cluster, traitSet, input, condition);
        assert getConvention() instanceof BindableConvention;
      }
  
      /** Creates a BindableFilter. */
      public static BindableFilter create(final RelNode input,
          RexNode condition) {
        final RelOptCluster cluster = input.getCluster();
        final RelMetadataQuery mq = cluster.getMetadataQuery();
        final RelTraitSet traitSet =
            cluster.traitSetOf(BindableConvention.INSTANCE)
                .replaceIfs(RelCollationTraitDef.INSTANCE,
                    () -> RelMdCollation.filter(mq, input));
        return new BindableFilter(cluster, traitSet, input, condition);
      }
  
      @Override public BindableFilter copy(RelTraitSet traitSet, RelNode input,
          RexNode condition) {
        return new BindableFilter(getCluster(), traitSet, input, condition);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new FilterNode(implementor.compiler, this);
      }
    }
  
    /**
     * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject}
     * to a {@link BindableProject}.
     *
     * @see #BINDABLE_PROJECT_RULE
     */
    public static class BindableProjectRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalProject.class, p -> !p.containsOver(),
              Convention.NONE, BindableConvention.INSTANCE,
              "BindableProjectRule")
          .withRuleFactory(BindableProjectRule::new);
  
      /** Called from the Config. */
      protected BindableProjectRule(Config config) {
        super(config);
      }
  
      @Override public boolean matches(RelOptRuleCall call) {
        final LogicalProject project = call.rel(0);
        return project.getVariablesSet().isEmpty();
      }
  
      @Override public RelNode convert(RelNode rel) {
        final LogicalProject project = (LogicalProject) rel;
        return new BindableProject(rel.getCluster(),
            rel.getTraitSet().replace(BindableConvention.INSTANCE),
            convert(project.getInput(),
                project.getInput().getTraitSet()
                    .replace(BindableConvention.INSTANCE)),
            project.getProjects(),
            project.getRowType());
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Project} in
     * bindable calling convention. */
    public static class BindableProject extends Project implements BindableRel {
      public BindableProject(RelOptCluster cluster, RelTraitSet traitSet,
          RelNode input, List<? extends RexNode> projects, RelDataType rowType) {
        super(cluster, traitSet, ImmutableList.of(), input, projects, rowType, ImmutableSet.of());
        assert getConvention() instanceof BindableConvention;
      }
  
      @Override public BindableProject copy(RelTraitSet traitSet, RelNode input,
          List<RexNode> projects, RelDataType rowType) {
        return new BindableProject(getCluster(), traitSet, input,
            projects, rowType);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new ProjectNode(implementor.compiler, this);
      }
    }
  
    /**
     * Rule to convert an {@link org.apache.calcite.rel.core.Sort} to a
     * {@link org.apache.calcite.interpreter.Bindables.BindableSort}.
     *
     * @see #BINDABLE_SORT_RULE
     */
    public static class BindableSortRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(Sort.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableSortRule")
          .withRuleFactory(BindableSortRule::new);
  
      /** Called from the Config. */
      protected BindableSortRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        final Sort sort = (Sort) rel;
        final RelTraitSet traitSet =
            sort.getTraitSet().replace(BindableConvention.INSTANCE);
        final RelNode input = sort.getInput();
        return new BindableSort(rel.getCluster(), traitSet,
            convert(input,
                input.getTraitSet().replace(BindableConvention.INSTANCE)),
            sort.getCollation(), sort.offset, sort.fetch);
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Sort}
     * bindable calling convention. */
    public static class BindableSort extends Sort implements BindableRel {
      public BindableSort(RelOptCluster cluster, RelTraitSet traitSet,
          RelNode input, RelCollation collation, @Nullable RexNode offset, @Nullable RexNode fetch) {
        super(cluster, traitSet, input, collation, offset, fetch);
        assert getConvention() instanceof BindableConvention;
      }
  
      @Override public BindableSort copy(RelTraitSet traitSet, RelNode newInput,
          RelCollation newCollation, @Nullable RexNode offset, @Nullable RexNode fetch) {
        return new BindableSort(getCluster(), traitSet, newInput, newCollation,
            offset, fetch);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new SortNode(implementor.compiler, this);
      }
    }
  
    /**
     * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalJoin}
     * to a {@link BindableJoin}.
     *
     * @see #BINDABLE_JOIN_RULE
     */
    public static class BindableJoinRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalJoin.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableJoinRule")
          .withRuleFactory(BindableJoinRule::new);
  
      /** Called from the Config. */
      protected BindableJoinRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        final LogicalJoin join = (LogicalJoin) rel;
        final BindableConvention out = BindableConvention.INSTANCE;
        final RelTraitSet traitSet = join.getTraitSet().replace(out);
        return new BindableJoin(rel.getCluster(), traitSet,
            convert(join.getLeft(),
                join.getLeft().getTraitSet()
                    .replace(BindableConvention.INSTANCE)),
            convert(join.getRight(),
                join.getRight().getTraitSet()
                    .replace(BindableConvention.INSTANCE)),
            join.getCondition(), join.getVariablesSet(), join.getJoinType());
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Join} in
     * bindable calling convention. */
    public static class BindableJoin extends Join implements BindableRel {
      /** Creates a BindableJoin. */
      protected BindableJoin(RelOptCluster cluster, RelTraitSet traitSet,
          RelNode left, RelNode right, RexNode condition,
          Set<CorrelationId> variablesSet, JoinRelType joinType) {
        super(cluster, traitSet, ImmutableList.of(), left, right,
            condition, variablesSet, joinType);
      }
  
      @Deprecated // to be removed before 2.0
      protected BindableJoin(RelOptCluster cluster, RelTraitSet traitSet,
          RelNode left, RelNode right, RexNode condition, JoinRelType joinType,
          Set<String> variablesStopped) {
        this(cluster, traitSet, left, right, condition,
            CorrelationId.setOf(variablesStopped), joinType);
      }
  
      @Override public BindableJoin copy(RelTraitSet traitSet, RexNode conditionExpr,
          RelNode left, RelNode right, JoinRelType joinType,
          boolean semiJoinDone) {
        return new BindableJoin(getCluster(), traitSet, left, right,
            conditionExpr, variablesSet, joinType);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new JoinNode(implementor.compiler, this);
      }
    }
  
    /**
     * Rule to convert an {@link SetOp} to a {@link BindableUnion}
     * or {@link BindableIntersect} or {@link BindableMinus}.
     *
     * @see #BINDABLE_SET_OP_RULE
     */
    public static class BindableSetOpRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(SetOp.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableSetOpRule")
          .withRuleFactory(BindableSetOpRule::new);
  
      /** Called from the Config. */
      protected BindableSetOpRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        final SetOp setOp = (SetOp) rel;
        final BindableConvention out = BindableConvention.INSTANCE;
        final RelTraitSet traitSet = setOp.getTraitSet().replace(out);
        if (setOp instanceof LogicalUnion) {
          return new BindableUnion(rel.getCluster(), traitSet,
              convertList(setOp.getInputs(), out), setOp.all);
        } else if (setOp instanceof LogicalIntersect) {
          return new BindableIntersect(rel.getCluster(), traitSet,
              convertList(setOp.getInputs(), out), setOp.all);
        } else {
          return new BindableMinus(rel.getCluster(), traitSet,
              convertList(setOp.getInputs(), out), setOp.all);
        }
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Union} in
     * bindable calling convention. */
    public static class BindableUnion extends Union implements BindableRel {
      public BindableUnion(RelOptCluster cluster, RelTraitSet traitSet,
          List<RelNode> inputs, boolean all) {
        super(cluster, traitSet, inputs, all);
      }
  
      @Override public BindableUnion copy(RelTraitSet traitSet, List<RelNode> inputs,
          boolean all) {
        return new BindableUnion(getCluster(), traitSet, inputs, all);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new SetOpNode(implementor.compiler, this);
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Intersect} in
     * bindable calling convention. */
    public static class BindableIntersect extends Intersect implements BindableRel {
      public BindableIntersect(RelOptCluster cluster, RelTraitSet traitSet,
          List<RelNode> inputs, boolean all) {
        super(cluster, traitSet, inputs, all);
      }
  
      @Override public BindableIntersect copy(RelTraitSet traitSet, List<RelNode> inputs,
          boolean all) {
        return new BindableIntersect(getCluster(), traitSet, inputs, all);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new SetOpNode(implementor.compiler, this);
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Minus} in
     * bindable calling convention. */
    public static class BindableMinus extends Minus implements BindableRel {
      public BindableMinus(RelOptCluster cluster, RelTraitSet traitSet,
          List<RelNode> inputs, boolean all) {
        super(cluster, traitSet, inputs, all);
      }
  
      @Override public BindableMinus copy(RelTraitSet traitSet, List<RelNode> inputs, boolean all) {
        return new BindableMinus(getCluster(), traitSet, inputs, all);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new SetOpNode(implementor.compiler, this);
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Values}
     * in bindable calling convention. */
    public static class BindableValues extends Values implements BindableRel {
      BindableValues(RelOptCluster cluster, RelDataType rowType,
          ImmutableList<ImmutableList<RexLiteral>> tuples, RelTraitSet traitSet) {
        super(cluster, rowType, tuples, traitSet);
      }
  
      @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
        assert inputs.isEmpty();
        return new BindableValues(getCluster(), getRowType(), tuples, traitSet);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new ValuesNode(implementor.compiler, this);
      }
    }
  
    /** Rule that converts a {@link Values} to bindable convention.
     *
     * @see #BINDABLE_VALUES_RULE */
    public static class BindableValuesRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalValues.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableValuesRule")
          .withRuleFactory(BindableValuesRule::new);
  
      /** Called from the Config. */
      protected BindableValuesRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        LogicalValues values = (LogicalValues) rel;
        return new BindableValues(values.getCluster(), values.getRowType(),
            values.getTuples(),
            values.getTraitSet().replace(BindableConvention.INSTANCE));
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Aggregate}
     * in bindable calling convention. */
    public static class BindableAggregate extends Aggregate
        implements BindableRel {
      public BindableAggregate(
          RelOptCluster cluster,
          RelTraitSet traitSet,
          RelNode input,
          ImmutableBitSet groupSet,
          @Nullable List<ImmutableBitSet> groupSets,
          List<AggregateCall> aggCalls)
          throws InvalidRelException {
        super(cluster, traitSet, ImmutableList.of(), input, groupSet, groupSets, aggCalls);
        assert getConvention() instanceof BindableConvention;
  
        for (AggregateCall aggCall : aggCalls) {
          if (aggCall.isDistinct()) {
            throw new InvalidRelException(
                "distinct aggregation not supported");
          }
          if (aggCall.distinctKeys != null) {
            throw new InvalidRelException(
                "within-distinct aggregation not supported");
          }
          AggImplementor implementor2 =
              RexImpTable.INSTANCE.get(aggCall.getAggregation(), false);
          if (implementor2 == null) {
            throw new InvalidRelException(
                "aggregation " + aggCall.getAggregation() + " not supported");
          }
        }
      }
  
      @Deprecated // to be removed before 2.0
      public BindableAggregate(RelOptCluster cluster, RelTraitSet traitSet,
          RelNode input, boolean indicator, ImmutableBitSet groupSet,
          List<ImmutableBitSet> groupSets, List<AggregateCall> aggCalls)
          throws InvalidRelException {
        this(cluster, traitSet, input, groupSet, groupSets, aggCalls);
        checkIndicator(indicator);
      }
  
      @Override public BindableAggregate copy(RelTraitSet traitSet, RelNode input,
          ImmutableBitSet groupSet,
          @Nullable List<ImmutableBitSet> groupSets, List<AggregateCall> aggCalls) {
        try {
          return new BindableAggregate(getCluster(), traitSet, input,
              groupSet, groupSets, aggCalls);
        } catch (InvalidRelException e) {
          // Semantic error not possible. Must be a bug. Convert to
          // internal error.
          throw new AssertionError(e);
        }
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new AggregateNode(implementor.compiler, this);
      }
    }
  
    /** Rule that converts an {@link Aggregate} to bindable convention.
     *
     * @see #BINDABLE_AGGREGATE_RULE */
    public static class BindableAggregateRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalAggregate.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableAggregateRule")
          .withRuleFactory(BindableAggregateRule::new);
  
      /** Called from the Config. */
      protected BindableAggregateRule(Config config) {
        super(config);
      }
  
      @Override public @Nullable RelNode convert(RelNode rel) {
        final LogicalAggregate agg = (LogicalAggregate) rel;
        final RelTraitSet traitSet =
            agg.getTraitSet().replace(BindableConvention.INSTANCE);
        try {
          return new BindableAggregate(rel.getCluster(), traitSet,
              convert(agg.getInput(), traitSet), false, agg.getGroupSet(),
              agg.getGroupSets(), agg.getAggCallList());
        } catch (InvalidRelException e) {
          RelOptPlanner.LOGGER.debug(e.toString());
          return null;
        }
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Window}
     * in bindable convention. */
    public static class BindableWindow extends Window implements BindableRel {
      /** Creates a BindableWindow. */
      BindableWindow(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
          List<RexLiteral> constants, RelDataType rowType, List<Group> groups) {
        super(cluster, traitSet, input, constants, rowType, groups);
      }
  
      @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
        return new BindableWindow(getCluster(), traitSet, sole(inputs),
            constants, getRowType(), groups);
      }
  
      @Override public Window copy(List<RexLiteral> constants) {
        return new BindableWindow(getCluster(), traitSet, getInput(),
            constants, getRowType(), groups);
      }
  
      @Override public @Nullable RelOptCost computeSelfCost(RelOptPlanner planner,
          RelMetadataQuery mq) {
        RelOptCost cost = super.computeSelfCost(planner, mq);
        if (cost == null) {
          return null;
        }
        return cost.multiplyBy(BindableConvention.COST_MULTIPLIER);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new WindowNode(implementor.compiler, this);
      }
    }
  
    /** Rule to convert a {@link org.apache.calcite.rel.logical.LogicalWindow}
     * to a {@link BindableWindow}.
     *
     * @see #BINDABLE_WINDOW_RULE */
    public static class BindableWindowRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalWindow.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableWindowRule")
          .withRuleFactory(BindableWindowRule::new);
  
      /** Called from the Config. */
      protected BindableWindowRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        final LogicalWindow winAgg = (LogicalWindow) rel;
        final RelTraitSet traitSet =
            winAgg.getTraitSet().replace(BindableConvention.INSTANCE);
        final RelNode input = winAgg.getInput();
        final RelNode convertedInput =
            convert(input,
                input.getTraitSet().replace(BindableConvention.INSTANCE));
        return new BindableWindow(rel.getCluster(), traitSet, convertedInput,
            winAgg.getConstants(), winAgg.getRowType(), winAgg.groups);
      }
    }
  
    /** Implementation of {@link org.apache.calcite.rel.core.Match}
     * in bindable convention. */
    public static class BindableMatch extends Match implements BindableRel {
      /** Singleton instance of BindableMatch. */
      BindableMatch(RelOptCluster cluster, RelTraitSet traitSet, RelNode input,
          RelDataType rowType, RexNode pattern, boolean strictStart,
          boolean strictEnd, Map<String, RexNode> patternDefinitions,
          Map<String, RexNode> measures, RexNode after,
          Map<String, ? extends SortedSet<String>> subsets, boolean allRows,
          ImmutableBitSet partitionKeys, RelCollation orderKeys,
          @Nullable RexNode interval) {
        super(cluster, traitSet, input, rowType, pattern, strictStart, strictEnd,
            patternDefinitions, measures, after, subsets, allRows, partitionKeys,
            orderKeys, interval);
      }
  
      @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
        return new BindableMatch(getCluster(), traitSet, inputs.get(0), getRowType(),
            pattern, strictStart, strictEnd, patternDefinitions, measures, after,
            subsets, allRows, partitionKeys, orderKeys, interval);
      }
  
      @Override public Class<Object[]> getElementType() {
        return Object[].class;
      }
  
      @Override public Enumerable<@Nullable Object[]> bind(DataContext dataContext) {
        return help(dataContext, this);
      }
  
      @Override public Node implement(InterpreterImplementor implementor) {
        return new MatchNode(implementor.compiler, this);
      }
    }
  
    /**
     * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalMatch}
     * to a {@link BindableMatch}.
     *
     * @see #BINDABLE_MATCH_RULE
     */
    public static class BindableMatchRule extends ConverterRule {
      /** Default configuration. */
      public static final Config DEFAULT_CONFIG = Config.INSTANCE
          .withConversion(LogicalMatch.class, Convention.NONE,
              BindableConvention.INSTANCE, "BindableMatchRule")
          .withRuleFactory(BindableMatchRule::new);
  
      /** Called from the Config. */
      protected BindableMatchRule(Config config) {
        super(config);
      }
  
      @Override public RelNode convert(RelNode rel) {
        final LogicalMatch match = (LogicalMatch) rel;
        final RelTraitSet traitSet =
            match.getTraitSet().replace(BindableConvention.INSTANCE);
        final RelNode input = match.getInput();
        final RelNode convertedInput =
            convert(input,
                input.getTraitSet().replace(BindableConvention.INSTANCE));
        return new BindableMatch(rel.getCluster(), traitSet, convertedInput,
            match.getRowType(), match.getPattern(), match.isStrictStart(),
            match.isStrictEnd(), match.getPatternDefinitions(),
            match.getMeasures(), match.getAfter(), match.getSubsets(),
            match.isAllRows(), match.getPartitionKeys(), match.getOrderKeys(),
            match.getInterval());
      }
    }
  
  }
  