Java Code Examples for org.apache.calcite.tools.RelBuilder#filter()

The following examples show how to use org.apache.calcite.tools.RelBuilder#filter() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: LoptOptimizeJoinRule.java    From Bats with Apache License 2.0 6 votes vote down vote up
/**
 * Determines whether any additional filters are applicable to a join tree.
 * If there are any, creates a filter node on top of the join tree with the
 * additional filters.
 *
 * @param relBuilder Builder holding current join tree
 * @param multiJoin join factors being optimized
 * @param left left side of join tree
 * @param right right side of join tree
 * @param filtersToAdd remaining filters
 */
private void addAdditionalFilters(
    RelBuilder relBuilder,
    LoptMultiJoin multiJoin,
    LoptJoinTree left,
    LoptJoinTree right,
    List<RexNode> filtersToAdd) {
  RexNode filterCond =
      addFilters(multiJoin, left, -1, right, filtersToAdd, false);
  if (!filterCond.isAlwaysTrue()) {
    // adjust the filter to reflect the outer join output
    int [] adjustments = new int[multiJoin.getNumTotalFields()];
    if (needsAdjustment(multiJoin, adjustments, left, right, false)) {
      RexBuilder rexBuilder =
          multiJoin.getMultiJoinRel().getCluster().getRexBuilder();
      filterCond =
          filterCond.accept(
              new RelOptUtil.RexInputConverter(
                  rexBuilder,
                  multiJoin.getMultiJoinFields(),
                  relBuilder.peek().getRowType().getFieldList(),
                  adjustments));
      relBuilder.filter(filterCond);
    }
  }
}
 
Example 2
Source File: IncrementalUpdateUtils.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
@Override
public RelNode visit(final LogicalFilter filter) {
  final RelBuilder relBuilder = newCalciteRelBuilderWithoutContext(filter.getCluster());
  RelNode input = filter.getInput().accept(this);
  relBuilder.push(input);

  RexNode newCondition = filter.getCondition().accept(new RexShuttle() {
    @Override
    public RexNode visitInputRef(RexInputRef inputRef) {
      return relBuilder.field(filter.getRowType().getFieldNames().get(inputRef.getIndex()));
    }
  });

  relBuilder.filter(newCondition);
  return relBuilder.build();
}
 
Example 3
Source File: LoptOptimizeJoinRule.java    From calcite with Apache License 2.0 6 votes vote down vote up
/**
 * Determines whether any additional filters are applicable to a join tree.
 * If there are any, creates a filter node on top of the join tree with the
 * additional filters.
 *
 * @param relBuilder Builder holding current join tree
 * @param multiJoin join factors being optimized
 * @param left left side of join tree
 * @param right right side of join tree
 * @param filtersToAdd remaining filters
 */
private void addAdditionalFilters(
    RelBuilder relBuilder,
    LoptMultiJoin multiJoin,
    LoptJoinTree left,
    LoptJoinTree right,
    List<RexNode> filtersToAdd) {
  RexNode filterCond =
      addFilters(multiJoin, left, -1, right, filtersToAdd, false);
  if (!filterCond.isAlwaysTrue()) {
    // adjust the filter to reflect the outer join output
    int [] adjustments = new int[multiJoin.getNumTotalFields()];
    if (needsAdjustment(multiJoin, adjustments, left, right, false)) {
      RexBuilder rexBuilder =
          multiJoin.getMultiJoinRel().getCluster().getRexBuilder();
      filterCond =
          filterCond.accept(
              new RelOptUtil.RexInputConverter(
                  rexBuilder,
                  multiJoin.getMultiJoinFields(),
                  relBuilder.peek().getRowType().getFieldList(),
                  adjustments));
      relBuilder.filter(filterCond);
    }
  }
}
 
Example 4
Source File: RelBuilderTest.java    From calcite with Apache License 2.0 6 votes vote down vote up
@Test void testRelBuilderToString() {
  final RelBuilder builder = RelBuilder.create(config().build());
  builder.scan("EMP");

  // One entry on the stack, a single-node tree
  final String expected1 = "LogicalTableScan(table=[[scott, EMP]])\n";
  assertThat(Util.toLinux(builder.toString()), is(expected1));

  // One entry on the stack, a two-node tree
  builder.filter(builder.equals(builder.field(2), builder.literal(3)));
  final String expected2 = "LogicalFilter(condition=[=($2, 3)])\n"
      + "  LogicalTableScan(table=[[scott, EMP]])\n";
  assertThat(Util.toLinux(builder.toString()), is(expected2));

  // Two entries on the stack
  builder.scan("DEPT");
  final String expected3 = "LogicalTableScan(table=[[scott, DEPT]])\n"
      + "LogicalFilter(condition=[=($2, 3)])\n"
      + "  LogicalTableScan(table=[[scott, EMP]])\n";
  assertThat(Util.toLinux(builder.toString()), is(expected3));
}
 
Example 5
Source File: CalcSplitRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Calc calc = call.rel(0);
  final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter =
      calc.getProgram().split();
  final RelBuilder relBuilder = call.builder();
  relBuilder.push(calc.getInput());
  relBuilder.filter(projectFilter.right);
  relBuilder.project(projectFilter.left, calc.getRowType().getFieldNames());
  call.transformTo(relBuilder.build());
}
 
Example 6
Source File: CalcSplitRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Calc calc = call.rel(0);
  final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter =
      calc.getProgram().split();
  final RelBuilder relBuilder = call.builder();
  relBuilder.push(calc.getInput());
  relBuilder.filter(projectFilter.right);
  relBuilder.project(projectFilter.left, calc.getRowType().getFieldNames());
  call.transformTo(relBuilder.build());
}
 
Example 7
Source File: LoptOptimizeJoinRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Creates the topmost projection that will sit on top of the selected join
 * ordering. The projection needs to match the original join ordering. Also,
 * places any post-join filters on top of the project.
 *
 * @param multiJoin join factors being optimized
 * @param joinTree selected join ordering
 * @param fieldNames field names corresponding to the projection expressions
 *
 * @return created projection
 */
private RelNode createTopProject(
    RelBuilder relBuilder,
    LoptMultiJoin multiJoin,
    LoptJoinTree joinTree,
    List<String> fieldNames) {
  List<RexNode> newProjExprs = new ArrayList<>();
  RexBuilder rexBuilder =
      multiJoin.getMultiJoinRel().getCluster().getRexBuilder();

  // create a projection on top of the joins, matching the original
  // join order
  final List<Integer> newJoinOrder = joinTree.getTreeOrder();
  int nJoinFactors = multiJoin.getNumJoinFactors();
  List<RelDataTypeField> fields = multiJoin.getMultiJoinFields();

  // create a mapping from each factor to its field offset in the join
  // ordering
  final Map<Integer, Integer> factorToOffsetMap = new HashMap<>();
  for (int pos = 0, fieldStart = 0; pos < nJoinFactors; pos++) {
    factorToOffsetMap.put(newJoinOrder.get(pos), fieldStart);
    fieldStart +=
        multiJoin.getNumFieldsInJoinFactor(newJoinOrder.get(pos));
  }

  for (int currFactor = 0; currFactor < nJoinFactors; currFactor++) {
    // if the factor is the right factor in a removable self-join,
    // then where possible, remap references to the right factor to
    // the corresponding reference in the left factor
    Integer leftFactor = null;
    if (multiJoin.isRightFactorInRemovableSelfJoin(currFactor)) {
      leftFactor = multiJoin.getOtherSelfJoinFactor(currFactor);
    }
    for (int fieldPos = 0;
        fieldPos < multiJoin.getNumFieldsInJoinFactor(currFactor);
        fieldPos++) {
      int newOffset = factorToOffsetMap.get(currFactor) + fieldPos;
      if (leftFactor != null) {
        Integer leftOffset =
            multiJoin.getRightColumnMapping(currFactor, fieldPos);
        if (leftOffset != null) {
          newOffset =
              factorToOffsetMap.get(leftFactor) + leftOffset;
        }
      }
      newProjExprs.add(
          rexBuilder.makeInputRef(
              fields.get(newProjExprs.size()).getType(),
              newOffset));
    }
  }

  relBuilder.push(joinTree.getJoinTree());
  relBuilder.project(newProjExprs, fieldNames);

  // Place the post-join filter (if it exists) on top of the final
  // projection.
  RexNode postJoinFilter =
      multiJoin.getMultiJoinRel().getPostJoinFilter();
  if (postJoinFilter != null) {
    relBuilder.filter(postJoinFilter);
  }
  return relBuilder.build();
}
 
Example 8
Source File: FilterCorrelateRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Filter filter = call.rel(0);
  final Correlate corr = call.rel(1);

  final List<RexNode> aboveFilters =
      RelOptUtil.conjunctions(filter.getCondition());

  final List<RexNode> leftFilters = new ArrayList<>();
  final List<RexNode> rightFilters = new ArrayList<>();

  // Try to push down above filters. These are typically where clause
  // filters. They can be pushed down if they are not on the NULL
  // generating side.
  RelOptUtil.classifyFilters(
      corr,
      aboveFilters,
      JoinRelType.INNER,
      false,
      !corr.getJoinType().toJoinType().generatesNullsOnLeft(),
      !corr.getJoinType().toJoinType().generatesNullsOnRight(),
      aboveFilters,
      leftFilters,
      rightFilters);

  if (leftFilters.isEmpty()
      && rightFilters.isEmpty()) {
    // no filters got pushed
    return;
  }

  // Create Filters on top of the children if any filters were
  // pushed to them.
  final RexBuilder rexBuilder = corr.getCluster().getRexBuilder();
  final RelBuilder relBuilder = call.builder();
  final RelNode leftRel =
      relBuilder.push(corr.getLeft()).filter(leftFilters).build();
  final RelNode rightRel =
      relBuilder.push(corr.getRight()).filter(rightFilters).build();

  // Create the new Correlate
  RelNode newCorrRel =
      corr.copy(corr.getTraitSet(), ImmutableList.of(leftRel, rightRel));

  call.getPlanner().onCopy(corr, newCorrRel);

  if (!leftFilters.isEmpty()) {
    call.getPlanner().onCopy(filter, leftRel);
  }
  if (!rightFilters.isEmpty()) {
    call.getPlanner().onCopy(filter, rightRel);
  }

  // Create a Filter on top of the join if needed
  relBuilder.push(newCorrRel);
  relBuilder.filter(
      RexUtil.fixUp(rexBuilder, aboveFilters,
          RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType())));

  call.transformTo(relBuilder.build());
}
 
Example 9
Source File: JoinNormalizationRule.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
/**
 * Attempt to create a new join with a canonicalized join expression, and a possible filter
 * on top
 * @param builder
 * @param join
 * @return a new join tree (or same as original argument if no change)
 */
private RelNode getNewJoinCondition(RelBuilder builder, Join join) {
  final List<Integer> leftKeys = Lists.newArrayList();
  final List<Integer> rightKeys = Lists.newArrayList();
  final List<Boolean> filterNulls = Lists.newArrayList();

  final RexNode remaining = RelOptUtil.splitJoinCondition(join.getLeft(), join.getRight(), join.getCondition(), leftKeys, rightKeys, filterNulls);
  final boolean hasEquiJoins = leftKeys.size() == rightKeys.size() && leftKeys.size() > 0 ;
  final JoinRelType joinType = join.getJoinType();

  // If join has no equi-join condition, do not transform
  if (!hasEquiJoins) {
    return join;
  }

  // Create a new partial condition for the equi-join
  final RexNode partialCondition = JoinFilterCanonicalizationRule.buildJoinCondition(
      builder.getRexBuilder(),
      join.getLeft().getRowType(),
      join.getRight().getRowType(),
      leftKeys,
      rightKeys,
      filterNulls);

  // We do not know how to add filter for non-INNER joins (see DRILL-1337)
  if (joinType != JoinRelType.INNER) {
    final RexNode newJoinCondition = RexUtil.composeConjunction(builder.getRexBuilder(), ImmutableList.of(partialCondition, remaining), false);
    if (RexUtil.eq(join.getCondition(), newJoinCondition)) {
      // Condition is the same, do not create a new rel
      return join;
    }
    builder.pushAll(ImmutableList.of(join.getLeft(), join.getRight()));
    builder.join(joinType, newJoinCondition);

    return builder.build();
  }

  // Check if join condition has changed if pure equi-join
  if (remaining.isAlwaysTrue() && RexUtil.eq(join.getCondition(), partialCondition)) {
    return join;
  }

  // Return the new join with a filter on top
  builder.pushAll(ImmutableList.of(join.getLeft(), join.getRight()));
  builder.join(joinType, partialCondition);
  builder.filter(remaining);
  return builder.build();
}
 
Example 10
Source File: PushFilterPastProjectRule.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
@Override
public void onMatch(RelOptRuleCall call) {
  Filter filterRel = call.rel(0);
  Project projRel = call.rel(1);

  // get a conjunctions of the filter condition. For each conjunction, if it refers to ITEM or FLATTEN expression
  // then we could not pushed down. Otherwise, it's qualified to be pushed down.
  final List<RexNode> predList = RelOptUtil.conjunctions(filterRel.getCondition());

  final List<RexNode> qualifiedPredList = Lists.newArrayList();
  final List<RexNode> unqualifiedPredList = Lists.newArrayList();


  for (final RexNode pred : predList) {
    if (qualifies(pred, projRel.getProjects())) {
      qualifiedPredList.add(pred);
    } else {
      unqualifiedPredList.add(pred);
    }
  }

  final RexNode qualifedPred = RexUtil.composeConjunction(filterRel.getCluster().getRexBuilder(), qualifiedPredList, true);

  if (qualifedPred == null) {
    return;
  }

  // convert the filter to one that references the child of the project
  RexNode newCondition =
      RelOptUtil.pushPastProject(qualifedPred, projRel);

  RelBuilder relBuilder = relBuilderFactory.create(filterRel.getCluster(), null);
  relBuilder.push(projRel.getInput());
  relBuilder.filter(newCondition);
  relBuilder.project(Pair.left(projRel.getNamedProjects()), Pair.right(projRel.getNamedProjects()));

  final RexNode unqualifiedPred = RexUtil.composeConjunction(filterRel.getCluster().getRexBuilder(), unqualifiedPredList, true);

  if (unqualifiedPred == null) {
    call.transformTo(relBuilder.build());
  } else {
    // if there are filters not qualified to be pushed down, then we have to put those filters on top of
    // the new Project operator.
    // Filter -- unqualified filters
    //   \
    //    Project
    //     \
    //      Filter  -- qualified filters
    relBuilder.filter(unqualifiedPred);
    call.transformTo(relBuilder.build());
  }
}
 
Example 11
Source File: MatchTest.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Tests the ElasticSearch match query. The match query is translated from
 * CONTAINS query which is build using RelBuilder, RexBuilder because the
 * normal SQL query assumes CONTAINS query is for date/period range.
 *
 * <p>Equivalent SQL query:
 *
 * <blockquote>
 * <code>select * from zips where city contains 'waltham'</code>
 * </blockquote>
 *
 * <p>ElasticSearch query for it:
 *
 * <blockquote><code>
 * {"query":{"constant_score":{"filter":{"match":{"city":"waltham"}}}}}
 * </code></blockquote>
 */
@Test void testMatchQuery() throws Exception {

  CalciteConnection con = (CalciteConnection) newConnectionFactory()
      .createConnection();
  SchemaPlus postSchema = con.getRootSchema().getSubSchema("elastic");

  FrameworkConfig postConfig = Frameworks.newConfigBuilder()
      .parserConfig(SqlParser.Config.DEFAULT)
      .defaultSchema(postSchema)
      .build();

  final RelBuilder builder = RelBuilder.create(postConfig);
  builder.scan(ZIPS);

  final RelDataTypeFactory typeFactory =
      new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
  final RexBuilder rexBuilder = new RexBuilder(typeFactory);

  RexNode nameRexNode = rexBuilder.makeCall(SqlStdOperatorTable.ITEM,
      rexBuilder.makeInputRef(typeFactory.createSqlType(SqlTypeName.ANY), 0),
      rexBuilder.makeCharLiteral(
          new NlsString("city", typeFactory.getDefaultCharset().name(),
              SqlCollation.COERCIBLE)));

  RelDataType mapType = typeFactory.createMapType(
      typeFactory.createSqlType(SqlTypeName.VARCHAR),
      typeFactory.createTypeWithNullability(
          typeFactory.createSqlType(SqlTypeName.ANY), true));

  List<RexNode> namedList =
      ImmutableList.of(rexBuilder.makeInputRef(mapType, 0),
          nameRexNode);

  // Add fields in builder stack so it is accessible while filter preparation
  builder.projectNamed(namedList, Arrays.asList("_MAP", "city"), true);

  RexNode filterRexNode = builder
      .call(SqlStdOperatorTable.CONTAINS, builder.field("city"),
          builder.literal("waltham"));
  builder.filter(filterRexNode);

  String builderExpected = ""
      + "LogicalFilter(condition=[CONTAINS($1, 'waltham')])\n"
      + "  LogicalProject(_MAP=[$0], city=[ITEM($0, 'city')])\n"
      + "    ElasticsearchTableScan(table=[[elastic, " + ZIPS + "]])\n";

  RelNode root = builder.build();

  RelRunner ru = (RelRunner) con.unwrap(Class.forName("org.apache.calcite.tools.RelRunner"));
  try (PreparedStatement preparedStatement = ru.prepare(root)) {

    String s = CalciteAssert.toString(preparedStatement.executeQuery());
    final String result = ""
        + "_MAP={id=02154, city=NORTH WALTHAM, loc=[-71.236497, 42.382492], "
        + "pop=57871, state=MA}; city=NORTH WALTHAM\n";

    // Validate query prepared
    assertThat(root, hasTree(builderExpected));

    // Validate result returned from ES
    assertThat(s, is(result));
  }
}
 
Example 12
Source File: LoptOptimizeJoinRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Creates the topmost projection that will sit on top of the selected join
 * ordering. The projection needs to match the original join ordering. Also,
 * places any post-join filters on top of the project.
 *
 * @param multiJoin join factors being optimized
 * @param joinTree selected join ordering
 * @param fieldNames field names corresponding to the projection expressions
 *
 * @return created projection
 */
private RelNode createTopProject(
    RelBuilder relBuilder,
    LoptMultiJoin multiJoin,
    LoptJoinTree joinTree,
    List<String> fieldNames) {
  List<RexNode> newProjExprs = new ArrayList<>();
  RexBuilder rexBuilder =
      multiJoin.getMultiJoinRel().getCluster().getRexBuilder();

  // create a projection on top of the joins, matching the original
  // join order
  final List<Integer> newJoinOrder = joinTree.getTreeOrder();
  int nJoinFactors = multiJoin.getNumJoinFactors();
  List<RelDataTypeField> fields = multiJoin.getMultiJoinFields();

  // create a mapping from each factor to its field offset in the join
  // ordering
  final Map<Integer, Integer> factorToOffsetMap = new HashMap<>();
  for (int pos = 0, fieldStart = 0; pos < nJoinFactors; pos++) {
    factorToOffsetMap.put(newJoinOrder.get(pos), fieldStart);
    fieldStart +=
        multiJoin.getNumFieldsInJoinFactor(newJoinOrder.get(pos));
  }

  for (int currFactor = 0; currFactor < nJoinFactors; currFactor++) {
    // if the factor is the right factor in a removable self-join,
    // then where possible, remap references to the right factor to
    // the corresponding reference in the left factor
    Integer leftFactor = null;
    if (multiJoin.isRightFactorInRemovableSelfJoin(currFactor)) {
      leftFactor = multiJoin.getOtherSelfJoinFactor(currFactor);
    }
    for (int fieldPos = 0;
        fieldPos < multiJoin.getNumFieldsInJoinFactor(currFactor);
        fieldPos++) {
      int newOffset = factorToOffsetMap.get(currFactor) + fieldPos;
      if (leftFactor != null) {
        Integer leftOffset =
            multiJoin.getRightColumnMapping(currFactor, fieldPos);
        if (leftOffset != null) {
          newOffset =
              factorToOffsetMap.get(leftFactor) + leftOffset;
        }
      }
      newProjExprs.add(
          rexBuilder.makeInputRef(
              fields.get(newProjExprs.size()).getType(),
              newOffset));
    }
  }

  relBuilder.push(joinTree.getJoinTree());
  relBuilder.project(newProjExprs, fieldNames);

  // Place the post-join filter (if it exists) on top of the final
  // projection.
  RexNode postJoinFilter =
      multiJoin.getMultiJoinRel().getPostJoinFilter();
  if (postJoinFilter != null) {
    relBuilder.filter(postJoinFilter);
  }
  return relBuilder.build();
}
 
Example 13
Source File: FilterCorrelateRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Filter filter = call.rel(0);
  final Correlate corr = call.rel(1);

  final List<RexNode> aboveFilters =
      RelOptUtil.conjunctions(filter.getCondition());

  final List<RexNode> leftFilters = new ArrayList<>();
  final List<RexNode> rightFilters = new ArrayList<>();

  // Try to push down above filters. These are typically where clause
  // filters. They can be pushed down if they are not on the NULL
  // generating side.
  RelOptUtil.classifyFilters(
      corr,
      aboveFilters,
      corr.getJoinType(),
      false,
      true,
      !corr.getJoinType().generatesNullsOnRight(),
      aboveFilters,
      leftFilters,
      rightFilters);

  if (leftFilters.isEmpty()
      && rightFilters.isEmpty()) {
    // no filters got pushed
    return;
  }

  // Create Filters on top of the children if any filters were
  // pushed to them.
  final RexBuilder rexBuilder = corr.getCluster().getRexBuilder();
  final RelBuilder relBuilder = call.builder();
  final RelNode leftRel =
      relBuilder.push(corr.getLeft()).filter(leftFilters).build();
  final RelNode rightRel =
      relBuilder.push(corr.getRight()).filter(rightFilters).build();

  // Create the new Correlate
  RelNode newCorrRel =
      corr.copy(corr.getTraitSet(), ImmutableList.of(leftRel, rightRel));

  call.getPlanner().onCopy(corr, newCorrRel);

  if (!leftFilters.isEmpty()) {
    call.getPlanner().onCopy(filter, leftRel);
  }
  if (!rightFilters.isEmpty()) {
    call.getPlanner().onCopy(filter, rightRel);
  }

  // Create a Filter on top of the join if needed
  relBuilder.push(newCorrRel);
  relBuilder.filter(
      RexUtil.fixUp(rexBuilder, aboveFilters,
          RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType())));

  call.transformTo(relBuilder.build());
}
 
Example 14
Source File: ProjectFilterTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Project origProject;
  final Filter filter;
  if (call.rels.length >= 2) {
    origProject = call.rel(0);
    filter = call.rel(1);
  } else {
    origProject = null;
    filter = call.rel(0);
  }
  final RelNode input = filter.getInput();
  final RexNode origFilter = filter.getCondition();

  if ((origProject != null)
      && RexOver.containsOver(origProject.getProjects(), null)) {
    // Cannot push project through filter if project contains a windowed
    // aggregate -- it will affect row counts. Abort this rule
    // invocation; pushdown will be considered after the windowed
    // aggregate has been implemented. It's OK if the filter contains a
    // windowed aggregate.
    return;
  }

  if ((origProject != null)
      && origProject.getRowType().isStruct()
      && origProject.getRowType().getFieldList().stream()
        .anyMatch(RelDataTypeField::isDynamicStar)) {
    // The PushProjector would change the plan:
    //
    //    prj(**=[$0])
    //    : - filter
    //        : - scan
    //
    // to form like:
    //
    //    prj(**=[$0])                    (1)
    //    : - filter                      (2)
    //        : - prj(**=[$0], ITEM= ...) (3)
    //            :  - scan
    // This new plan has more cost that the old one, because of the new
    // redundant project (3), if we also have FilterProjectTransposeRule in
    // the rule set, it will also trigger infinite match of the ProjectMergeRule
    // for project (1) and (3).
    return;
  }

  final RelBuilder builder = call.builder();
  final RelNode topProject;
  if (origProject != null && (wholeProject || wholeFilter)) {
    builder.push(input);

    final Set<RexNode> set = new LinkedHashSet<>();
    final RelOptUtil.InputFinder refCollector = new RelOptUtil.InputFinder();

    if (wholeFilter) {
      set.add(filter.getCondition());
    } else {
      filter.getCondition().accept(refCollector);
    }
    if (wholeProject) {
      set.addAll(origProject.getProjects());
    } else {
      refCollector.visitEach(origProject.getProjects());
    }

    // Build a list with inputRefs, in order, first, then other expressions.
    final List<RexNode> list = new ArrayList<>();
    final ImmutableBitSet refs = refCollector.build();
    for (RexNode field : builder.fields()) {
      if (refs.get(((RexInputRef) field).getIndex()) || set.contains(field)) {
        list.add(field);
      }
    }
    set.removeAll(list);
    list.addAll(set);
    builder.project(list);
    final Replacer replacer = new Replacer(list, builder);
    builder.filter(replacer.visit(filter.getCondition()));
    builder.project(replacer.visitList(origProject.getProjects()),
        origProject.getRowType().getFieldNames());
    topProject = builder.build();
  } else {
    // The traditional mode of operation of this rule: push down field
    // references. The effect is similar to RelFieldTrimmer.
    final PushProjector pushProjector =
        new PushProjector(origProject, origFilter, input,
            preserveExprCondition, builder);
    topProject = pushProjector.convertProject(null);
  }

  if (topProject != null) {
    call.transformTo(topProject);
  }
}