This is an automated email from the ASF dual-hosted git repository.

jhyde pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git

commit 59d6eb852676ab5d77eccd63f488c9f121804d5d
Author: Julian Hyde <[email protected]>
AuthorDate: Fri May 29 10:18:58 2020 -0700

    [CALCITE-4038] Refactor RexVisitor, RexBiVisitor, RelOptUtil.InputFinder
    
    Make mutable field RelOptUtil.InputFinder.inputBitSet private.
    The field still exists, public and deprecated, but it shadows
    a new private field. Rather than calling InputFinder.inputBitSet.build()
    you should now call InputFinder.build().
    
    In RexVisitor and RexBiVisitor add methods visitList and
    visitEach (the former returns a list, and the latter returns
    void).
    
    Deprecate RexShuttle.apply in favor of visitList.
    
    Add RexWindowBound.accept(RexBiVisitor).
    
    For RexBiVisitor<X, Integer> add a method visitEachIndexed,
    the index passed as the 'payload' argument.
    
    Add abstract implementations of RexBiVisitor:
    RexUnaryBiVisitor and RexBiVisitorImpl.
---
 .../java/org/apache/calcite/plan/RelOptUtil.java   |  42 ++++++--
 .../java/org/apache/calcite/rel/core/Match.java    |  22 ++--
 .../apache/calcite/rel/logical/LogicalCalc.java    |   5 +-
 .../apache/calcite/rel/logical/LogicalWindow.java  |   7 +-
 .../calcite/rel/metadata/RelMdAllPredicates.java   |   4 +-
 .../calcite/rel/metadata/RelMdColumnOrigins.java   |   2 +-
 .../rel/metadata/RelMdExpressionLineage.java       |   2 +-
 .../apache/calcite/rel/rules/DateRangeRules.java   |   4 +-
 .../calcite/rel/rules/JoinAssociateRule.java       |   6 +-
 .../apache/calcite/rel/rules/LoptMultiJoin.java    |   2 +-
 .../calcite/rel/rules/ProjectTableScanRule.java    |  19 ++--
 .../calcite/rel/rules/ProjectToWindowRule.java     |  19 ++--
 .../rel/rules/ProjectWindowTransposeRule.java      |  14 +--
 .../rel/rules/UnionPullUpConstantsRule.java        |   4 +-
 .../materialize/MaterializedViewAggregateRule.java |   6 +-
 .../java/org/apache/calcite/rex/LogicVisitor.java  |  46 ++------
 .../java/org/apache/calcite/rex/RexAnalyzer.java   |   2 +-
 .../java/org/apache/calcite/rex/RexBiVisitor.java  |  40 +++++++
 .../org/apache/calcite/rex/RexBiVisitorImpl.java   | 119 +++++++++++++++++++++
 .../org/apache/calcite/rex/RexInterpreter.java     |   6 +-
 .../org/apache/calcite/rex/RexProgramBuilder.java  |   4 +-
 .../java/org/apache/calcite/rex/RexShuttle.java    |  15 +--
 .../java/org/apache/calcite/rex/RexSimplify.java   |   7 +-
 .../org/apache/calcite/rex/RexUnaryBiVisitor.java  |  88 +++++++++++++++
 .../main/java/org/apache/calcite/rex/RexUtil.java  |  29 ++---
 .../java/org/apache/calcite/rex/RexVisitor.java    |  27 +++++
 .../org/apache/calcite/rex/RexVisitorImpl.java     |   4 +-
 .../org/apache/calcite/rex/RexWindowBound.java     |  13 +++
 .../apache/calcite/sql2rel/RelFieldTrimmer.java    |  12 +--
 .../sql2rel/RelStructuredTypeFlattener.java        |   7 +-
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |   8 +-
 .../java/org/apache/calcite/tools/RelBuilder.java  |   2 +-
 .../main/java/org/apache/calcite/util/Util.java    |  17 ++-
 .../java/org/apache/calcite/util/UtilTest.java     |  13 +++
 .../apache/calcite/adapter/druid/DruidRules.java   |  28 ++---
 .../adapter/elasticsearch/ElasticsearchRules.java  |   9 --
 .../adapter/elasticsearch/PredicateAnalyzer.java   |  15 +--
 .../calcite/adapter/geode/rel/GeodeRules.java      |  11 +-
 .../apache/calcite/adapter/mongodb/MongoRules.java |   9 --
 .../calcite/piglet/PigToSqlAggregateRule.java      |   4 +-
 40 files changed, 443 insertions(+), 250 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java 
b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 2bf32cd..8ace22d 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -2751,7 +2751,7 @@ public abstract class RelOptUtil {
     final List<RexNode> filtersToRemove = new ArrayList<>();
     for (RexNode filter : filters) {
       final InputFinder inputFinder = InputFinder.analyze(filter);
-      final ImmutableBitSet inputBits = inputFinder.inputBitSet.build();
+      final ImmutableBitSet inputBits = inputFinder.build();
 
       // REVIEW - are there any expressions that need special handling
       // and therefore cannot be pushed?
@@ -2974,9 +2974,7 @@ public abstract class RelOptUtil {
    */
   public static List<RexNode> pushPastProject(List<? extends RexNode> nodes,
       Project project) {
-    final List<RexNode> list = new ArrayList<>();
-    pushShuttle(project).visitList(nodes, list);
-    return list;
+    return pushShuttle(project).visitList(nodes);
   }
 
   /** As {@link #pushPastProject}, but returns null if the resulting 
expressions
@@ -4244,17 +4242,33 @@ public abstract class RelOptUtil {
    * Visitor which builds a bitmap of the inputs used by an expression.
    */
   public static class InputFinder extends RexVisitorImpl<Void> {
+    /** @deprecated Being replaced by private field {@link #bitBuilder}.
+     * Use {@link #build}. */
+    @Deprecated // to be removed before 1.25
     public final ImmutableBitSet.Builder inputBitSet;
+
+    private final ImmutableBitSet.Builder bitBuilder;
     private final Set<RelDataTypeField> extraFields;
 
+    private InputFinder(Set<RelDataTypeField> extraFields,
+        ImmutableBitSet.Builder bitBuilder) {
+      super(true);
+      this.bitBuilder = bitBuilder;
+      this.inputBitSet = bitBuilder; // deprecated field mirrors private field
+      this.extraFields = extraFields;
+    }
+
     public InputFinder() {
       this(null);
     }
 
     public InputFinder(Set<RelDataTypeField> extraFields) {
-      super(true);
-      this.inputBitSet = ImmutableBitSet.builder();
-      this.extraFields = extraFields;
+      this(extraFields, ImmutableBitSet.builder());
+    }
+
+    public InputFinder(Set<RelDataTypeField> extraFields,
+        ImmutableBitSet initialBits) {
+      this(extraFields, initialBits.rebuild());
     }
 
     /** Returns an input finder that has analyzed a given expression. */
@@ -4268,7 +4282,7 @@ public abstract class RelOptUtil {
      * Returns a bit set describing the inputs used by an expression.
      */
     public static ImmutableBitSet bits(RexNode node) {
-      return analyze(node).inputBitSet.build();
+      return analyze(node).build();
     }
 
     /**
@@ -4278,11 +4292,19 @@ public abstract class RelOptUtil {
     public static ImmutableBitSet bits(List<RexNode> exprs, RexNode expr) {
       final InputFinder inputFinder = new InputFinder();
       RexUtil.apply(inputFinder, exprs, expr);
-      return inputFinder.inputBitSet.build();
+      return inputFinder.build();
+    }
+
+    /** Returns the bit set.
+     *
+     * <p>After calling this method, you cannot do any more visits or call this
+     * method again. */
+    public ImmutableBitSet build() {
+      return bitBuilder.build();
     }
 
     public Void visitInputRef(RexInputRef inputRef) {
-      inputBitSet.set(inputRef.getIndex());
+      bitBuilder.set(inputRef.getIndex());
       return null;
     }
 
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Match.java 
b/core/src/main/java/org/apache/calcite/rel/core/Match.java
index a985e5b..12fe3b9 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Match.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Match.java
@@ -209,7 +209,7 @@ public abstract class Match extends SingleRel {
   /**
    * Find aggregate functions in operands.
    */
-  private static class AggregateFinder extends RexVisitorImpl {
+  private static class AggregateFinder extends RexVisitorImpl<Void> {
     final SortedSet<RexMRAggCall> aggregateCalls = new TreeSet<>();
     final Map<String, SortedSet<RexMRAggCall>> aggregateCallsPerVar =
         new TreeMap<>();
@@ -218,7 +218,7 @@ public abstract class Match extends SingleRel {
       super(true);
     }
 
-    @Override public Object visitCall(RexCall call) {
+    @Override public Void visitCall(RexCall call) {
       SqlAggFunction aggFunction = null;
       switch (call.getKind()) {
       case SUM:
@@ -243,9 +243,7 @@ public abstract class Match extends SingleRel {
         aggFunction = new SqlBitOpAggFunction(call.getKind());
         break;
       default:
-        for (RexNode rex : call.getOperands()) {
-          rex.accept(this);
-        }
+        visitEach(call.operands);
       }
       if (aggFunction != null) {
         RexMRAggCall aggCall = new RexMRAggCall(aggFunction,
@@ -287,22 +285,20 @@ public abstract class Match extends SingleRel {
    * Visits the operands of an aggregate call to retrieve relevant pattern
    * variables.
    */
-  private static class PatternVarFinder extends RexVisitorImpl {
+  private static class PatternVarFinder extends RexVisitorImpl<Void> {
     final Set<String> patternVars = new HashSet<>();
 
     PatternVarFinder() {
       super(true);
     }
 
-    @Override public Object visitPatternFieldRef(RexPatternFieldRef fieldRef) {
+    @Override public Void visitPatternFieldRef(RexPatternFieldRef fieldRef) {
       patternVars.add(fieldRef.getAlpha());
       return null;
     }
 
-    @Override public Object visitCall(RexCall call) {
-      for (RexNode node : call.getOperands()) {
-        node.accept(this);
-      }
+    @Override public Void visitCall(RexCall call) {
+      visitEach(call.operands);
       return null;
     }
 
@@ -312,9 +308,7 @@ public abstract class Match extends SingleRel {
     }
 
     public Set<String> go(List<RexNode> rexNodeList) {
-      for (RexNode rex : rexNodeList) {
-        rex.accept(this);
-      }
+      visitEach(rexNodeList);
       return patternVars;
     }
   }
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java 
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
index 05a0b4e..8e5d2ac 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalCalc.java
@@ -33,7 +33,6 @@ import org.apache.calcite.rel.metadata.RelMdDistribution;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.rules.FilterToCalcRule;
 import org.apache.calcite.rel.rules.ProjectToCalcRule;
-import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.util.Util;
 
@@ -133,9 +132,7 @@ public final class LogicalCalc extends Calc {
   @Override public void collectVariablesUsed(Set<CorrelationId> variableSet) {
     final RelOptUtil.VariableUsedVisitor vuv =
         new RelOptUtil.VariableUsedVisitor(null);
-    for (RexNode expr : program.getExprList()) {
-      expr.accept(vuv);
-    }
+    vuv.visitEach(program.getExprList());
     variableSet.addAll(vuv.variables);
   }
 
diff --git 
a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java 
b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index cc7d5ad..7272a57 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -263,11 +263,8 @@ public final class LogicalWindow extends Window {
     // partitions may not match the order in which they occurred in the
     // original expression.
     // Add a project to permute them.
-    final List<RexNode> rexNodesWindow = new ArrayList<>();
-    for (RexNode rexNode : program.getExprList()) {
-      rexNodesWindow.add(rexNode.accept(shuttle));
-    }
-    final List<RexNode> refToWindow = toInputRefs(rexNodesWindow);
+    final List<RexNode> refToWindow =
+        toInputRefs(shuttle.visitList(program.getExprList()));
 
     final List<RexNode> projectList = new ArrayList<>();
     for (RexLocalRef inputRef : program.getProjectList()) {
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
index f034057..7945780 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
@@ -134,7 +134,7 @@ public class RelMdAllPredicates
     final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
     final RelOptUtil.InputFinder inputFinder = new 
RelOptUtil.InputFinder(inputExtraFields);
     pred.accept(inputFinder);
-    final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+    final ImmutableBitSet inputFieldsUsed = inputFinder.build();
 
     // Infer column origin expressions for given references
     final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
@@ -212,7 +212,7 @@ public class RelMdAllPredicates
     final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
     final RelOptUtil.InputFinder inputFinder = new 
RelOptUtil.InputFinder(inputExtraFields);
     pred.accept(inputFinder);
-    final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+    final ImmutableBitSet inputFieldsUsed = inputFinder.build();
 
     // Infer column origin expressions for given references
     final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
index d2b2d18..2958c12 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdColumnOrigins.java
@@ -270,7 +270,7 @@ public class RelMdColumnOrigins
   private Set<RelColumnOrigin> getMultipleColumns(RexNode rexNode, RelNode 
input,
       final RelMetadataQuery mq) {
     final Set<RelColumnOrigin> set = new HashSet<>();
-    RexVisitor visitor =
+    final RexVisitor<Void> visitor =
         new RexVisitorImpl<Void>(true) {
           public Void visitInputRef(RexInputRef inputRef) {
             Set<RelColumnOrigin> inputSet =
diff --git 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
index fea2fa7..7f495b1 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
@@ -493,6 +493,6 @@ public class RelMdExpressionLineage
     final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
     final RelOptUtil.InputFinder inputFinder = new 
RelOptUtil.InputFinder(inputExtraFields);
     expr.accept(inputFinder);
-    return inputFinder.inputBitSet.build();
+    return inputFinder.build();
   }
 }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java 
b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
index 9029ab2..9e1dac4 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/DateRangeRules.java
@@ -195,7 +195,7 @@ public abstract class DateRangeRules {
 
   /** Visitor that searches for calls to {@code EXTRACT}, {@code FLOOR} or
    * {@code CEIL}, building a list of distinct time units. */
-  private static class ExtractFinder extends RexVisitorImpl
+  private static class ExtractFinder extends RexVisitorImpl<Void>
       implements AutoCloseable {
     private final Set<TimeUnitRange> timeUnits =
         EnumSet.noneOf(TimeUnitRange.class);
@@ -208,7 +208,7 @@ public abstract class DateRangeRules {
       super(true);
     }
 
-    @Override public Object visitCall(RexCall call) {
+    @Override public Void visitCall(RexCall call) {
       switch (call.getKind()) {
       case EXTRACT:
         final RexLiteral operand = (RexLiteral) call.getOperands().get(0);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/JoinAssociateRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/JoinAssociateRule.java
index e90746e..3be093f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/JoinAssociateRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/JoinAssociateRule.java
@@ -133,9 +133,9 @@ public class JoinAssociateRule extends RelOptRule 
implements TransformationRule
             aCount + bCount + cCount,
             0, aCount, bCount,
             bCount, aCount + bCount, cCount);
-    final List<RexNode> newBottomList = new ArrayList<>();
-    new RexPermuteInputsShuttle(bottomMapping, relB, relC)
-        .visitList(bottom, newBottomList);
+    final List<RexNode> newBottomList =
+        new RexPermuteInputsShuttle(bottomMapping, relB, relC)
+            .visitList(bottom);
     RexNode newBottomCondition =
         RexUtil.composeConjunction(rexBuilder, newBottomList);
 
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java 
b/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
index 855bfdc..f49644f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/LoptMultiJoin.java
@@ -453,7 +453,7 @@ public class LoptMultiJoin {
   private ImmutableBitSet fieldBitmap(RexNode joinFilter) {
     final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder();
     joinFilter.accept(inputFinder);
-    return inputFinder.inputBitSet.build();
+    return inputFinder.build();
   }
 
   /**
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectTableScanRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectTableScanRule.java
index 23788a8..ce288ae 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectTableScanRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectTableScanRule.java
@@ -109,16 +109,15 @@ public abstract class ProjectTableScanRule extends 
RelOptRule {
     assert table.unwrap(ProjectableFilterableTable.class) != null;
 
     final List<Integer> selectedColumns = new ArrayList<>();
-    project.getProjects().forEach(proj -> {
-      proj.accept(new RexVisitorImpl<Void>(true) {
-        public Void visitInputRef(RexInputRef inputRef) {
-          if (!selectedColumns.contains(inputRef.getIndex())) {
-            selectedColumns.add(inputRef.getIndex());
-          }
-          return null;
+    final RexVisitorImpl<Void> visitor = new RexVisitorImpl<Void>(true) {
+      public Void visitInputRef(RexInputRef inputRef) {
+        if (!selectedColumns.contains(inputRef.getIndex())) {
+          selectedColumns.add(inputRef.getIndex());
         }
-      });
-    });
+        return null;
+      }
+    };
+    visitor.visitEach(project.getProjects());
 
     final List<RexNode> filtersPushDown;
     final List<Integer> projectsPushDown;
@@ -138,7 +137,7 @@ public abstract class ProjectTableScanRule extends 
RelOptRule {
     Mapping mapping =
         Mappings.target(selectedColumns, scan.getRowType().getFieldCount());
     final List<RexNode> newProjectRexNodes =
-        ImmutableList.copyOf(RexUtil.apply(mapping, project.getProjects()));
+        RexUtil.apply(mapping, project.getProjects());
 
     if (RexUtil.isIdentity(newProjectRexNodes, newScan.getRowType())) {
       call.transformTo(newScan);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
index 53e3235..d61fb95 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.rel.rules;
 
-import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
@@ -28,6 +27,7 @@ import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalCalc;
 import org.apache.calcite.rel.logical.LogicalWindow;
+import org.apache.calcite.rex.RexBiVisitorImpl;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexDynamicParam;
 import org.apache.calcite.rex.RexFieldAccess;
@@ -36,7 +36,6 @@ import org.apache.calcite.rex.RexLocalRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexOver;
 import org.apache.calcite.rex.RexProgram;
-import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.rex.RexWindow;
 import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.tools.RelBuilderFactory;
@@ -362,15 +361,13 @@ public abstract class ProjectToWindowRule extends 
RelOptRule implements Transfor
         graph.addVertex(i);
       }
 
-      for (final Ord<RexNode> expr : Ord.zip(exprs)) {
-        expr.e.accept(
-            new RexVisitorImpl<Void>(true) {
-              public Void visitLocalRef(RexLocalRef localRef) {
-                graph.addEdge(localRef.getIndex(), expr.i);
-                return null;
-              }
-            });
-      }
+      new RexBiVisitorImpl<Void, Integer>(true) {
+        public Void visitLocalRef(RexLocalRef localRef, Integer i) {
+          graph.addEdge(localRef.getIndex(), i);
+          return null;
+        }
+      }.visitEachIndexed(exprs);
+
       assert graph.vertexSet().size() == exprs.size();
       return graph;
     }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
index edd688f..74b9a96 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectWindowTransposeRule.java
@@ -173,10 +173,8 @@ public class ProjectWindowTransposeRule extends RelOptRule 
implements Transforma
         window.constants, outputBuilder.build(), groups);
 
     // Modify the top LogicalProject
-    final List<RexNode> topProjExps = new ArrayList<>();
-    for (RexNode rexNode : project.getChildExps()) {
-      topProjExps.add(rexNode.accept(indexAdjustment));
-    }
+    final List<RexNode> topProjExps =
+        indexAdjustment.visitList(project.getChildExps());
 
     final LogicalProject newTopProj = project.copy(
         newLogicalWindow.getTraitSet(),
@@ -207,9 +205,7 @@ public class ProjectWindowTransposeRule extends RelOptRule 
implements Transforma
     };
 
     // Reference in LogicalProject
-    for (RexNode rexNode : project.getChildExps()) {
-      rexNode.accept(referenceFinder);
-    }
+    referenceFinder.visitEach(project.getChildExps());
 
     // Reference in LogicalWindow
     for (Window.Group group : window.groups) {
@@ -228,9 +224,7 @@ public class ProjectWindowTransposeRule extends RelOptRule 
implements Transforma
       }
 
       // Reference in Window Functions
-      for (Window.RexWinAggCall rexWinAggCall : group.aggCalls) {
-        rexWinAggCall.accept(referenceFinder);
-      }
+      referenceFinder.visitEach(group.aggCalls);
     }
     return beReferred.build();
   }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
index 2180612..ca6ffb1 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/UnionPullUpConstantsRule.java
@@ -35,8 +35,6 @@ import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.mapping.Mappings;
 
-import com.google.common.collect.ImmutableList;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -108,7 +106,7 @@ public class UnionPullUpConstantsRule extends RelOptRule 
implements Transformati
     // Update top Project positions
     final Mappings.TargetMapping mapping =
         RelOptUtil.permutation(refs, union.getInput(0).getRowType()).inverse();
-    topChildExprs = ImmutableList.copyOf(RexUtil.apply(mapping, 
topChildExprs));
+    topChildExprs = RexUtil.apply(mapping, topChildExprs);
 
     // Create new Project-Union-Project sequences
     final RelBuilder relBuilder = call.builder();
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
index f13e75d..f4c318d 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/materialize/MaterializedViewAggregateRule.java
@@ -414,10 +414,8 @@ public abstract class MaterializedViewAggregateRule 
extends MaterializedViewRule
       // We have a Project on top, gather only what is needed
       final RelOptUtil.InputFinder inputFinder =
           new RelOptUtil.InputFinder(new LinkedHashSet<>());
-      for (RexNode e : topProject.getChildExps()) {
-        e.accept(inputFinder);
-      }
-      references = inputFinder.inputBitSet.build();
+      inputFinder.visitEach(topProject.getChildExps());
+      references = inputFinder.build();
       for (int i = 0; i < queryAggregate.getGroupCount(); i++) {
         indexes.set(queryAggregate.getGroupSet().nth(i));
       }
diff --git a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java 
b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
index 59adf52..9bcd686 100644
--- a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
@@ -29,12 +29,13 @@ import java.util.Set;
 /**
  * Visitor pattern for traversing a tree of {@link RexNode} objects.
  */
-public class LogicVisitor implements RexBiVisitor<Logic, Logic> {
+public class LogicVisitor extends RexUnaryBiVisitor<Logic> {
   private final RexNode seek;
   private final Collection<Logic> logicCollection;
 
   /** Creates a LogicVisitor. */
   private LogicVisitor(RexNode seek, Collection<Logic> logicCollection) {
+    super(true);
     this.seek = seek;
     this.logicCollection = logicCollection;
   }
@@ -77,7 +78,7 @@ public class LogicVisitor implements RexBiVisitor<Logic, 
Logic> {
     Collections.replaceAll(logicList, Logic.FALSE, Logic.UNKNOWN_AS_TRUE);
   }
 
-  public Logic visitCall(RexCall call, Logic logic) {
+  @Override public Logic visitCall(RexCall call, Logic logic) {
     final Logic arg0 = logic;
     switch (call.getKind()) {
     case IS_NOT_NULL:
@@ -114,47 +115,22 @@ public class LogicVisitor implements RexBiVisitor<Logic, 
Logic> {
     return end(call, arg0);
   }
 
-  private Logic end(RexNode node, Logic arg) {
+  @Override protected Logic end(RexNode node, Logic arg) {
     if (node.equals(seek)) {
       logicCollection.add(arg);
     }
     return arg;
   }
 
-  public Logic visitInputRef(RexInputRef inputRef, Logic arg) {
-    return end(inputRef, arg);
-  }
-
-  public Logic visitLocalRef(RexLocalRef localRef, Logic arg) {
-    return end(localRef, arg);
-  }
-
-  public Logic visitLiteral(RexLiteral literal, Logic arg) {
-    return end(literal, arg);
-  }
-
-  public Logic visitOver(RexOver over, Logic arg) {
+  @Override public Logic visitOver(RexOver over, Logic arg) {
     return end(over, arg);
   }
 
-  public Logic visitCorrelVariable(RexCorrelVariable correlVariable,
-      Logic arg) {
-    return end(correlVariable, arg);
-  }
-
-  public Logic visitDynamicParam(RexDynamicParam dynamicParam, Logic arg) {
-    return end(dynamicParam, arg);
-  }
-
-  public Logic visitRangeRef(RexRangeRef rangeRef, Logic arg) {
-    return end(rangeRef, arg);
-  }
-
-  public Logic visitFieldAccess(RexFieldAccess fieldAccess, Logic arg) {
+  @Override public Logic visitFieldAccess(RexFieldAccess fieldAccess, Logic 
arg) {
     return end(fieldAccess, arg);
   }
 
-  public Logic visitSubQuery(RexSubQuery subQuery, Logic arg) {
+  @Override public Logic visitSubQuery(RexSubQuery subQuery, Logic arg) {
     if (!subQuery.getType().isNullable()) {
       if (arg == Logic.TRUE_FALSE_UNKNOWN) {
         arg = Logic.TRUE_FALSE;
@@ -162,12 +138,4 @@ public class LogicVisitor implements RexBiVisitor<Logic, 
Logic> {
     }
     return end(subQuery, arg);
   }
-
-  @Override public Logic visitTableInputRef(RexTableInputRef ref, Logic arg) {
-    return end(ref, arg);
-  }
-
-  @Override public Logic visitPatternFieldRef(RexPatternFieldRef ref, Logic 
arg) {
-    return end(ref, arg);
-  }
 }
diff --git a/core/src/main/java/org/apache/calcite/rex/RexAnalyzer.java 
b/core/src/main/java/org/apache/calcite/rex/RexAnalyzer.java
index 3b07ca7..0e059a2 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexAnalyzer.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexAnalyzer.java
@@ -46,7 +46,7 @@ public class RexAnalyzer {
     this.e = e;
     final VariableCollector variableCollector = new VariableCollector();
     e.accept(variableCollector);
-    predicates.pulledUpPredicates.forEach(p -> p.accept(variableCollector));
+    variableCollector.visitEach(predicates.pulledUpPredicates);
     variables = ImmutableList.copyOf(variableCollector.builder);
     unsupportedCount = variableCollector.unsupportedCount;
   }
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java 
b/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
index be2280d..65bef92 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
@@ -16,6 +16,11 @@
  */
 package org.apache.calcite.rex;
 
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Visitor pattern for traversing a tree of {@link RexNode} objects
  * and passing a payload to each.
@@ -51,4 +56,39 @@ public interface RexBiVisitor<R, P> {
   R visitTableInputRef(RexTableInputRef ref, P arg);
 
   R visitPatternFieldRef(RexPatternFieldRef ref, P arg);
+
+  /** Visits a list and writes the results to another list. */
+  default void visitList(Iterable<? extends RexNode> exprs, P arg,
+      List<R> out) {
+    for (RexNode expr : exprs) {
+      out.add(expr.accept(this, arg));
+    }
+  }
+
+  /** Visits a list and returns a list of the results.
+   * The resulting list is immutable and does not contain nulls. */
+  default List<R> visitList(Iterable<? extends RexNode> exprs, P arg) {
+    final List<R> out = new ArrayList<>();
+    visitList(exprs, arg, out);
+    return ImmutableList.copyOf(out);
+  }
+
+  /** Visits a list of expressions. */
+  default void visitEach(Iterable<? extends RexNode> exprs, P arg) {
+    for (RexNode expr : exprs) {
+      expr.accept(this, arg);
+    }
+  }
+
+  /** Visits a list of expressions, passing the 0-based index of the expression
+   * in the list.
+   *
+   * <p>Assumes that the payload type {@code P} is {@code Integer}. */
+  default void visitEachIndexed(Iterable<? extends RexNode> exprs) {
+    int i = 0;
+    for (RexNode expr : exprs) {
+      //noinspection unchecked
+      expr.accept(this, (P) (Integer) i++);
+    }
+  }
 }
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java 
b/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java
new file mode 100644
index 0000000..ed5ba26
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rex/RexBiVisitorImpl.java
@@ -0,0 +1,119 @@
+/*
+ * 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.rex;
+
+/**
+ * Default implementation of {@link RexBiVisitor}, which visits each node but
+ * does nothing while it's there.
+ *
+ * @param <R> Return type from each {@code visitXxx} method
+ * @param <P> Payload type
+ */
+public class RexBiVisitorImpl<R, P> implements RexBiVisitor<R, P> {
+  //~ Instance fields --------------------------------------------------------
+
+  protected final boolean deep;
+
+  //~ Constructors -----------------------------------------------------------
+
+  protected RexBiVisitorImpl(boolean deep) {
+    this.deep = deep;
+  }
+
+  //~ Methods ----------------------------------------------------------------
+
+  public R visitInputRef(RexInputRef inputRef, P arg) {
+    return null;
+  }
+
+  public R visitLocalRef(RexLocalRef localRef, P arg) {
+    return null;
+  }
+
+  public R visitLiteral(RexLiteral literal, P arg) {
+    return null;
+  }
+
+  public R visitOver(RexOver over, P arg) {
+    R r = visitCall(over, arg);
+    if (!deep) {
+      return null;
+    }
+    final RexWindow window = over.getWindow();
+    for (RexFieldCollation orderKey : window.orderKeys) {
+      orderKey.left.accept(this, arg);
+    }
+    for (RexNode partitionKey : window.partitionKeys) {
+      partitionKey.accept(this, arg);
+    }
+    window.getLowerBound().accept(this, arg);
+    window.getUpperBound().accept(this, arg);
+    return r;
+  }
+
+  public R visitCorrelVariable(RexCorrelVariable correlVariable, P arg) {
+    return null;
+  }
+
+  public R visitCall(RexCall call, P arg) {
+    if (!deep) {
+      return null;
+    }
+
+    R r = null;
+    for (RexNode operand : call.operands) {
+      r = operand.accept(this, arg);
+    }
+    return r;
+  }
+
+  public R visitDynamicParam(RexDynamicParam dynamicParam, P arg) {
+    return null;
+  }
+
+  public R visitRangeRef(RexRangeRef rangeRef, P arg) {
+    return null;
+  }
+
+  public R visitFieldAccess(RexFieldAccess fieldAccess, P arg) {
+    if (!deep) {
+      return null;
+    }
+    final RexNode expr = fieldAccess.getReferenceExpr();
+    return expr.accept(this, arg);
+  }
+
+  public R visitSubQuery(RexSubQuery subQuery, P arg) {
+    if (!deep) {
+      return null;
+    }
+
+    R r = null;
+    for (RexNode operand : subQuery.operands) {
+      r = operand.accept(this, arg);
+    }
+    return r;
+  }
+
+  @Override public R visitTableInputRef(RexTableInputRef ref, P arg) {
+    return null;
+  }
+
+  @Override public R visitPatternFieldRef(RexPatternFieldRef fieldRef, P arg) {
+    return null;
+  }
+}
diff --git a/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java 
b/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java
index e3c3240..11d91ae 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexInterpreter.java
@@ -28,7 +28,6 @@ import com.google.common.collect.ImmutableMap;
 
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.EnumSet;
 import java.util.List;
@@ -136,10 +135,7 @@ public class RexInterpreter implements 
RexVisitor<Comparable> {
   }
 
   public Comparable visitCall(RexCall call) {
-    final List<Comparable> values = new ArrayList<>(call.operands.size());
-    for (RexNode operand : call.operands) {
-      values.add(operand.accept(this));
-    }
+    final List<Comparable> values = visitList(call.operands);
     switch (call.getKind()) {
     case IS_NOT_DISTINCT_FROM:
       if (containsNull(values)) {
diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java 
b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
index 932aa3e..71ac25c 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java
@@ -111,9 +111,7 @@ public class RexProgramBuilder {
     // are normalizing, expressions will be registered if and when they are
     // first used.
     if (!normalize) {
-      for (RexNode expr : exprList) {
-        expr.accept(shuttle);
-      }
+      shuttle.visitEach(exprList);
     }
 
     final RexShuttle expander = new RexProgram.ExpansionShuttle(exprList);
diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java 
b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
index c898221..2aaabad 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
@@ -166,16 +166,6 @@ public class RexShuttle implements RexVisitor<RexNode> {
   }
 
   /**
-   * Visits a list and writes the results to another list.
-   */
-  public void visitList(
-      List<? extends RexNode> exprs, List<RexNode> outExprs) {
-    for (RexNode expr : exprs) {
-      outExprs.add(expr.accept(this));
-    }
-  }
-
-  /**
    * Visits each of a list of field collations and returns a list of the
    * results.
    *
@@ -271,9 +261,8 @@ public class RexShuttle implements RexVisitor<RexNode> {
     }
   }
 
-  /**
-   * Applies this shuttle to each expression in an iterable.
-   */
+  /** @deprecated Use {@link RexVisitor#visitList(Iterable)} if possible. */
+  @Deprecated // to be removed before 1.25
   public final Iterable<RexNode> apply(Iterable<? extends RexNode> iterable) {
     return Iterables.transform(iterable,
         t -> t == null ? null : t.accept(RexShuttle.this));
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java 
b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index f43da20..e569a7b 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -1134,12 +1134,7 @@ public class RexSimplify {
       if (!safeOps.contains(call.getKind())) {
         return false;
       }
-      for (RexNode o : call.getOperands()) {
-        if (!o.accept(this)) {
-          return false;
-        }
-      }
-      return true;
+      return RexVisitorImpl.visitArrayAnd(this, call.operands);
     }
 
     @Override public Boolean visitOver(RexOver over) {
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java 
b/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java
new file mode 100644
index 0000000..57007e4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rex/RexUnaryBiVisitor.java
@@ -0,0 +1,88 @@
+/*
+ * 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.rex;
+
+/**
+ * Default implementation of a {@link RexBiVisitor} whose payload and return
+ * type are the same.
+ *
+ * @param <R> Return type from each {@code visitXxx} method
+ */
+public class RexUnaryBiVisitor<R> extends RexBiVisitorImpl<R, R> {
+  /** Creates a RexUnaryBiVisitor. */
+  protected RexUnaryBiVisitor(boolean deep) {
+    super(deep);
+  }
+
+  /** Called as the last action of, and providing the result for,
+   *  each {@code visitXxx} method; derived classes may override. */
+  protected R end(RexNode e, R arg) {
+    return arg;
+  }
+
+  @Override public R visitInputRef(RexInputRef inputRef, R arg) {
+    return end(inputRef, arg);
+  }
+
+  @Override public R visitLocalRef(RexLocalRef localRef, R arg) {
+    return end(localRef, arg);
+  }
+
+  @Override public R visitTableInputRef(RexTableInputRef ref, R arg) {
+    return end(ref, arg);
+  }
+
+  @Override public R visitPatternFieldRef(RexPatternFieldRef fieldRef, R arg) {
+    return end(fieldRef, arg);
+  }
+
+  @Override public R visitLiteral(RexLiteral literal, R arg) {
+    return end(literal, arg);
+  }
+
+  @Override public R visitDynamicParam(RexDynamicParam dynamicParam, R arg) {
+    return end(dynamicParam, arg);
+  }
+
+  @Override public R visitRangeRef(RexRangeRef rangeRef, R arg) {
+    return end(rangeRef, arg);
+  }
+
+  @Override public R visitCorrelVariable(RexCorrelVariable correlVariable, R 
arg) {
+    return end(correlVariable, arg);
+  }
+
+  @Override public R visitOver(RexOver over, R arg) {
+    super.visitOver(over, arg);
+    return end(over, arg);
+  }
+
+  @Override public R visitCall(RexCall call, R arg) {
+    super.visitCall(call, arg);
+    return end(call, arg);
+  }
+
+  @Override public R visitFieldAccess(RexFieldAccess fieldAccess, R arg) {
+    super.visitFieldAccess(fieldAccess, arg);
+    return end(fieldAccess, arg);
+  }
+
+  @Override public R visitSubQuery(RexSubQuery subQuery, R arg) {
+    super.visitSubQuery(subQuery, arg);
+    return end(subQuery, arg);
+  }
+}
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java 
b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index 98ef1e2..67d60a5 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -1322,10 +1322,9 @@ public class RexUtil {
   /**
    * Applies a mapping to an iterable over expressions.
    */
-  public static Iterable<RexNode> apply(Mappings.TargetMapping mapping,
+  public static List<RexNode> apply(Mappings.TargetMapping mapping,
       Iterable<? extends RexNode> nodes) {
-    final RexPermuteInputsShuttle shuttle = 
RexPermuteInputsShuttle.of(mapping);
-    return Iterables.transform(nodes, e -> e.accept(shuttle));
+    return RexPermuteInputsShuttle.of(mapping).visitList(nodes);
   }
 
   /**
@@ -1646,8 +1645,8 @@ public class RexUtil {
   /**
    * Shifts every {@link RexInputRef} in an expression by {@code offset}.
    */
-  public static Iterable<RexNode> shift(Iterable<RexNode> nodes, int offset) {
-    return new RexShiftShuttle(offset).apply(nodes);
+  public static List<RexNode> shift(Iterable<RexNode> nodes, int offset) {
+    return new RexShiftShuttle(offset).visitList(nodes);
   }
 
   /**
@@ -2145,16 +2144,12 @@ public class RexUtil {
    */
   public static Set<RelTableRef> gatherTableReferences(final List<RexNode> 
nodes) {
     final Set<RelTableRef> occurrences = new HashSet<>();
-    RexVisitor<Void> visitor =
-        new RexVisitorImpl<Void>(true) {
-          @Override public Void visitTableInputRef(RexTableInputRef ref) {
-            occurrences.add(ref.getTableRef());
-            return super.visitTableInputRef(ref);
-          }
-        };
-    for (RexNode e : nodes) {
-      e.accept(visitor);
-    }
+    new RexVisitorImpl<Void>(true) {
+      @Override public Void visitTableInputRef(RexTableInputRef ref) {
+        occurrences.add(ref.getTableRef());
+        return super.visitTableInputRef(ref);
+      }
+    }.visitEach(nodes);
     return occurrences;
   }
 
@@ -2305,9 +2300,7 @@ public class RexUtil {
     }
 
     public Void visitCall(RexCall call) {
-      for (RexNode operand : call.operands) {
-        operand.accept(this);
-      }
+      visitEach(call.operands);
       return null;
     }
 
diff --git a/core/src/main/java/org/apache/calcite/rex/RexVisitor.java 
b/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
index b958aa1..b202ded 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
@@ -16,6 +16,11 @@
  */
 package org.apache.calcite.rex;
 
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Visitor pattern for traversing a tree of {@link RexNode} objects.
  *
@@ -51,4 +56,26 @@ public interface RexVisitor<R> {
   R visitTableInputRef(RexTableInputRef fieldRef);
 
   R visitPatternFieldRef(RexPatternFieldRef fieldRef);
+
+  /** Visits a list and writes the results to another list. */
+  default void visitList(Iterable<? extends RexNode> exprs, List<R> out) {
+    for (RexNode expr : exprs) {
+      out.add(expr.accept(this));
+    }
+  }
+
+  /** Visits a list and returns a list of the results.
+   * The resulting list is immutable and does not contain nulls. */
+  default List<R> visitList(Iterable<? extends RexNode> exprs) {
+    final List<R> out = new ArrayList<>();
+    visitList(exprs, out);
+    return ImmutableList.copyOf(out);
+  }
+
+  /** Visits a list of expressions. */
+  default void visitEach(Iterable<? extends RexNode> exprs) {
+    for (RexNode expr : exprs) {
+      expr.accept(this);
+    }
+  }
 }
diff --git a/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java 
b/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
index 707d583..c480878 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
@@ -58,9 +58,7 @@ public class RexVisitorImpl<R> implements RexVisitor<R> {
     for (RexFieldCollation orderKey : window.orderKeys) {
       orderKey.left.accept(this);
     }
-    for (RexNode partitionKey : window.partitionKeys) {
-      partitionKey.accept(this);
-    }
+    visitEach(window.partitionKeys);
     window.getLowerBound().accept(this);
     window.getUpperBound().accept(this);
     return r;
diff --git a/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java 
b/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java
index 3734b88..7f94b11 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexWindowBound.java
@@ -85,6 +85,7 @@ public abstract class RexWindowBound {
 
   /**
    * Transforms the bound via {@link org.apache.calcite.rex.RexVisitor}.
+   *
    * @param visitor visitor to accept
    * @param <R> return type of the visitor
    * @return transformed bound
@@ -94,6 +95,18 @@ public abstract class RexWindowBound {
   }
 
   /**
+   * Transforms the bound via {@link org.apache.calcite.rex.RexBiVisitor}.
+   *
+   * @param visitor visitor to accept
+   * @param arg Payload
+   * @param <R> return type of the visitor
+   * @return transformed bound
+   */
+  public <R, P> RexWindowBound accept(RexBiVisitor<R, P> visitor, P arg) {
+    return this;
+  }
+
+  /**
    * Returns the number of nodes in this bound.
    *
    * @see RexNode#nodeCount()
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java 
b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index be4df0f..2da00c0 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -372,7 +372,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
         ord.e.accept(inputFinder);
       }
     }
-    ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+    ImmutableBitSet inputFieldsUsed = inputFinder.build();
 
     // Create input with trimmed columns.
     TrimResult trimResult =
@@ -461,10 +461,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     final Set<RelDataTypeField> inputExtraFields =
         new LinkedHashSet<>(extraFields);
     RelOptUtil.InputFinder inputFinder =
-        new RelOptUtil.InputFinder(inputExtraFields);
-    inputFinder.inputBitSet.addAll(fieldsUsed);
+        new RelOptUtil.InputFinder(inputExtraFields, fieldsUsed);
     conditionExpr.accept(inputFinder);
-    final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+    final ImmutableBitSet inputFieldsUsed = inputFinder.build();
 
     // Create input with trimmed columns.
     TrimResult trimResult =
@@ -650,10 +649,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     final Set<RelDataTypeField> combinedInputExtraFields =
         new LinkedHashSet<>(extraFields);
     RelOptUtil.InputFinder inputFinder =
-        new RelOptUtil.InputFinder(combinedInputExtraFields);
-    inputFinder.inputBitSet.addAll(fieldsUsed);
+        new RelOptUtil.InputFinder(combinedInputExtraFields, fieldsUsed);
     conditionExpr.accept(inputFinder);
-    final ImmutableBitSet fieldsUsedPlus = inputFinder.inputBitSet.build();
+    final ImmutableBitSet fieldsUsedPlus = inputFinder.build();
 
     // If no system fields are used, we can remove them.
     int systemFieldUsedCount = 0;
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java 
b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
index 897eb63..1e9ba3d 100644
--- 
a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
+++ 
b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
@@ -91,7 +91,6 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.SortedMap;
 import java.util.SortedSet;
-import java.util.stream.Collectors;
 
 // TODO jvs 10-Feb-2005:  factor out generic rewrite helper, with the
 // ability to map between old and new rels and field ordinals.  Also,
@@ -688,10 +687,8 @@ public class RelStructuredTypeFlattener implements 
ReflectiveVisitor {
             }
           }
         } else {
-          List<RexNode> newOperands = operands.stream()
-              .map(op -> op.accept(shuttle))
-              .collect(Collectors.toList());
-          newExp = rexBuilder.makeCall(exp.getType(), operator, newOperands);
+          newExp = rexBuilder.makeCall(exp.getType(), operator,
+              shuttle.visitList(operands));
           // flatten call result type
           flattenResultTypeOfRexCall(newExp, fieldName, flattenedExps);
         }
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 28ffa01..fdb26df 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -201,7 +201,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.function.UnaryOperator;
 import java.util.stream.Collectors;
@@ -5926,15 +5925,10 @@ public class SqlToRelConverter {
     /** Whether to push down join conditions; default true. */
     public ConfigBuilder withPushJoinCondition(boolean pushJoinCondition) {
       return withRelBuilderConfigTransform(
-          compose(relBuilderConfigTransform,
+          Util.andThen(relBuilderConfigTransform,
               c -> c.withPushJoinCondition(pushJoinCondition)));
     }
 
-    private static <X> UnaryOperator<X> compose(Function<X, X> f1,
-        Function<X, X> f2) {
-      return x -> f2.apply(f1.apply(x));
-    }
-
     @Deprecated // to be removed before 2.0
     public ConfigBuilder withInSubqueryThreshold(int inSubQueryThreshold) {
       return withInSubQueryThreshold(inSubQueryThreshold);
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 9e19e74..77e10c0 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -1483,7 +1483,7 @@ public class RelBuilder {
    * <p>The default implementation returns {@code true};
    * sub-classes may disable merge by overriding to return {@code false}. */
   @Experimental
-  @Deprecated
+  @Deprecated // to be removed before 1.25
   protected boolean shouldMergeProject() {
     return true;
   }
diff --git a/core/src/main/java/org/apache/calcite/util/Util.java 
b/core/src/main/java/org/apache/calcite/util/Util.java
index 8b59a88..d5b9198 100644
--- a/core/src/main/java/org/apache/calcite/util/Util.java
+++ b/core/src/main/java/org/apache/calcite/util/Util.java
@@ -97,6 +97,7 @@ import java.util.StringTokenizer;
 import java.util.TimeZone;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
 import java.util.jar.JarFile;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -2374,8 +2375,12 @@ public class Util {
    * Returns a {@code Collector} that accumulates the input elements into a
    * Guava {@link ImmutableList} via a {@link ImmutableList.Builder}.
    *
-   * <p>It will be obsolete when we move to {@link Bug#upgrade Guava 21.0},
-   * which has {@code ImmutableList.toImmutableList()}.
+   * <p>It will be obsolete when we move to {@link Bug#upgrade Guava 28.0-jre}.
+   * Guava 21.0 introduced {@code ImmutableList.toImmutableList()}, but it had
+   * a {@link com.google.common.annotations.Beta} tag until 28.0-jre.
+   *
+   * <p>In {@link Bug#upgrade Guava 21.0}, change this method to call
+   * {@code ImmutableList.toImmutableList()}, ignoring the {@code @Beta} tag.
    *
    * @param <T> Type of the input elements
    *
@@ -2392,6 +2397,14 @@ public class Util {
         ImmutableList.Builder::build);
   }
 
+  /** Returns an operator that applies {@code op1} and then {@code op2}.
+   *
+   * <p>As {@link Function#andThen(Function)} but for {@link UnaryOperator}. */
+  public static <X> UnaryOperator<X> andThen(UnaryOperator<X> op1,
+      UnaryOperator<X> op2) {
+    return op1.andThen(op2)::apply;
+  }
+
   /** Transforms a list, applying a function to each element. */
   public static <F, T> List<T> transform(List<F> list,
       java.util.function.Function<F, T> function) {
diff --git a/core/src/test/java/org/apache/calcite/util/UtilTest.java 
b/core/src/test/java/org/apache/calcite/util/UtilTest.java
index d952fa7..fd6c1ea 100644
--- a/core/src/test/java/org/apache/calcite/util/UtilTest.java
+++ b/core/src/test/java/org/apache/calcite/util/UtilTest.java
@@ -90,6 +90,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.ObjIntConsumer;
+import java.util.function.UnaryOperator;
 
 import static org.apache.calcite.test.Matchers.isLinux;
 
@@ -2427,6 +2428,18 @@ class UtilTest {
     assertThat(isLinux("x\r\ny").matches("x\ny"), is(false));
   }
 
+  /** Tests {@link Util#andThen(UnaryOperator, UnaryOperator)}. */
+  @Test void testAndThen() {
+    final UnaryOperator<Integer> inc = x -> x + 1;
+    final UnaryOperator<Integer> triple = x -> x * 3;
+    final UnaryOperator<Integer> tripleInc = Util.andThen(triple, inc);
+    final UnaryOperator<Integer> incTriple = Util.andThen(inc, triple);
+    final Function<Integer, Integer> incTripleFn = inc.andThen(triple);
+    assertThat(tripleInc.apply(2), is(7));
+    assertThat(incTriple.apply(2), is(9));
+    assertThat(incTripleFn.apply(2), is(9));
+  }
+
   /** Tests {@link Util#transform(List, java.util.function.Function)}. */
   @Test void testTransform() {
     final List<String> beatles =
diff --git 
a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java 
b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
index 3e51f58..ce2a66f 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
@@ -332,12 +332,11 @@ public class DruidRules {
       call.transformTo(newProject2);
     }
 
-    private static Pair<List<RexNode>, List<RexNode>> splitProjects(final 
RexBuilder rexBuilder,
-            final RelNode input, List<RexNode> nodes) {
-      final RelOptUtil.InputReferencedVisitor visitor = new 
RelOptUtil.InputReferencedVisitor();
-      for (RexNode node : nodes) {
-        node.accept(visitor);
-      }
+    private static Pair<List<RexNode>, List<RexNode>> splitProjects(
+        final RexBuilder rexBuilder, final RelNode input, List<RexNode> nodes) 
{
+      final RelOptUtil.InputReferencedVisitor visitor =
+          new RelOptUtil.InputReferencedVisitor();
+      visitor.visitEach(nodes);
       if (visitor.inputPosReferenced.size() == 
input.getRowType().getFieldCount()) {
         // All inputs are referenced
         return null;
@@ -350,17 +349,12 @@ public class DruidRules {
         belowNodes.add(node);
         belowTypes.add(node.getType());
       }
-      final List<RexNode> aboveNodes = new ArrayList<>();
-      for (RexNode node : nodes) {
-        aboveNodes.add(
-            node.accept(
-              new RexShuttle() {
-                @Override public RexNode visitInputRef(RexInputRef ref) {
-                  final int index = positions.indexOf(ref.getIndex());
-                  return rexBuilder.makeInputRef(belowTypes.get(index), index);
-                }
-              }));
-      }
+      final List<RexNode> aboveNodes = new RexShuttle() {
+        @Override public RexNode visitInputRef(RexInputRef ref) {
+          final int index = positions.indexOf(ref.getIndex());
+          return rexBuilder.makeInputRef(belowTypes.get(index), index);
+        }
+      }.visitList(nodes);
       return Pair.of(aboveNodes, belowNodes);
     }
   }
diff --git 
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
 
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
index c70d935..7f2487c 100644
--- 
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
+++ 
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/ElasticsearchRules.java
@@ -43,7 +43,6 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
 
 import java.util.AbstractList;
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -182,14 +181,6 @@ class ElasticsearchRules {
       throw new IllegalArgumentException("Translation of " + call
           + " is not supported by ElasticsearchProject");
     }
-
-    List<String> visitList(List<RexNode> list) {
-      final List<String> strings = new ArrayList<>();
-      for (RexNode node: list) {
-        strings.add(node.accept(this));
-      }
-      return strings;
-    }
   }
 
   /**
diff --git 
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
 
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
index 1679f03..335f894 100644
--- 
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
+++ 
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
@@ -36,14 +36,12 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 
-import java.util.ArrayList;
 import java.util.GregorianCalendar;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
-import java.util.stream.Collectors;
 
 import static org.apache.calcite.adapter.elasticsearch.QueryBuilders.boolQuery;
 import static 
org.apache.calcite.adapter.elasticsearch.QueryBuilders.existsQuery;
@@ -133,11 +131,8 @@ class PredicateAnalyzer {
       if (call.getOperator().getKind() == SqlKind.NOT) {
         RexNode child = call.getOperands().get(0);
         if (child.getKind() == SqlKind.LIKE) {
-          List<RexNode> operands = ((RexCall) child).getOperands()
-              .stream()
-              .map(rexNode -> rexNode.accept(NotLikeConverter.this))
-              .collect(Collectors.toList());
-          return rexBuilder.makeCall(SqlStdOperatorTable.NOT_LIKE, operands);
+          return rexBuilder.makeCall(SqlStdOperatorTable.NOT_LIKE,
+              visitList(((RexCall) child).getOperands()));
         }
       }
       return super.visitCall(call);
@@ -246,11 +241,7 @@ class PredicateAnalyzer {
         }
       case FUNCTION:
         if (call.getOperator().getName().equalsIgnoreCase("CONTAINS")) {
-          List<Expression> operands = new ArrayList<>();
-          for (RexNode node : call.getOperands()) {
-            final Expression nodeExpr = node.accept(this);
-            operands.add(nodeExpr);
-          }
+          List<Expression> operands = visitList(call.getOperands());
           String query = convertQueryString(operands.subList(0, 
operands.size() - 1),
               operands.get(operands.size() - 1));
           return QueryExpression.create(new 
NamedFieldExpression()).queryString(query);
diff --git 
a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java 
b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java
index 80e9220..ab7f402 100644
--- a/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java
+++ b/geode/src/main/java/org/apache/calcite/adapter/geode/rel/GeodeRules.java
@@ -99,7 +99,8 @@ public class GeodeRules {
     }
 
     @Override public String visitCall(RexCall call) {
-      final List<String> strings = visitList(call.operands);
+      final List<String> strings = new ArrayList<>();
+      visitList(call.operands, strings);
       if (call.getOperator() == SqlStdOperatorTable.ITEM) {
         final RexNode op1 = call.getOperands().get(1);
         if (op1 instanceof RexLiteral) {
@@ -117,14 +118,6 @@ public class GeodeRules {
     private String stripQuotes(String s) {
       return s.startsWith("'") && s.endsWith("'") ? s.substring(1, s.length() 
- 1) : s;
     }
-
-    List<String> visitList(List<RexNode> list) {
-      final List<String> strings = new ArrayList<>();
-      for (RexNode node : list) {
-        strings.add(node.accept(this));
-      }
-      return strings;
-    }
   }
 
   /**
diff --git 
a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoRules.java 
b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoRules.java
index befa5ca..f065bfc 100644
--- a/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoRules.java
+++ b/mongodb/src/main/java/org/apache/calcite/adapter/mongodb/MongoRules.java
@@ -49,7 +49,6 @@ import org.apache.calcite.util.trace.CalciteTrace;
 import org.slf4j.Logger;
 
 import java.util.AbstractList;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -234,14 +233,6 @@ public class MongoRules {
           ? s.substring(1, s.length() - 1)
           : s;
     }
-
-    public List<String> visitList(List<RexNode> list) {
-      final List<String> strings = new ArrayList<>();
-      for (RexNode node : list) {
-        strings.add(node.accept(this));
-      }
-      return strings;
-    }
   }
 
   /** Base class for planner rules that convert a relational expression to
diff --git 
a/piglet/src/main/java/org/apache/calcite/piglet/PigToSqlAggregateRule.java 
b/piglet/src/main/java/org/apache/calcite/piglet/PigToSqlAggregateRule.java
index e584771..bd94628 100644
--- a/piglet/src/main/java/org/apache/calcite/piglet/PigToSqlAggregateRule.java
+++ b/piglet/src/main/java/org/apache/calcite/piglet/PigToSqlAggregateRule.java
@@ -101,9 +101,7 @@ public class PigToSqlAggregateRule extends RelOptRule {
       } else if (isMultisetProjection(call) && !ignoreMultisetProj) {
         pigAggCalls.add(call);
       }
-      for (RexNode operand : call.operands) {
-        operand.accept(this);
-      }
+      visitEach(call.operands);
       return null;
     }
 

Reply via email to