You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/09/28 22:19:33 UTC

[2/7] incubator-calcite git commit: [CALCITE-828] Use RelBuilder in rules rather than type-specific RelNode factories

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
index 2e6667c..477b74a 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/MultiJoinOptimizeBushyRule.java
@@ -18,7 +18,6 @@ package org.apache.calcite.rel.rules;
 
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.JoinRelType;
@@ -30,6 +29,8 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexPermuteInputsShuttle;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitor;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
@@ -37,7 +38,6 @@ import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import java.io.PrintWriter;
@@ -67,25 +67,26 @@ import java.util.List;
  */
 public class MultiJoinOptimizeBushyRule extends RelOptRule {
   public static final MultiJoinOptimizeBushyRule INSTANCE =
-      new MultiJoinOptimizeBushyRule(RelFactories.DEFAULT_JOIN_FACTORY,
-          RelFactories.DEFAULT_PROJECT_FACTORY);
+      new MultiJoinOptimizeBushyRule(RelFactories.LOGICAL_BUILDER);
 
-  private final RelFactories.JoinFactory joinFactory;
-  private final RelFactories.ProjectFactory projectFactory;
   private final PrintWriter pw =
       CalcitePrepareImpl.DEBUG ? new PrintWriter(System.out, true) : null;
 
   /** Creates an MultiJoinOptimizeBushyRule. */
+  public MultiJoinOptimizeBushyRule(RelBuilderFactory relBuilderFactory) {
+    super(operand(MultiJoin.class, any()), relBuilderFactory, null);
+  }
+
+  @Deprecated // to be removed before 2.0
   public MultiJoinOptimizeBushyRule(RelFactories.JoinFactory joinFactory,
       RelFactories.ProjectFactory projectFactory) {
-    super(operand(MultiJoin.class, any()));
-    this.joinFactory = joinFactory;
-    this.projectFactory = projectFactory;
+    this(RelBuilder.proto(joinFactory, projectFactory));
   }
 
   @Override public void onMatch(RelOptRuleCall call) {
     final MultiJoin multiJoinRel = call.rel(0);
     final RexBuilder rexBuilder = multiJoinRel.getCluster().getRexBuilder();
+    final RelBuilder relBuilder = call.builder();
 
     final LoptMultiJoin multiJoin = new LoptMultiJoin(multiJoinRel);
 
@@ -254,11 +255,12 @@ public class MultiJoinOptimizeBushyRule extends RelOptRule {
         final RexNode condition =
             RexUtil.composeConjunction(rexBuilder, joinVertex.conditions,
                 false);
-        relNodes.add(
-            Pair.of(
-                joinFactory.createJoin(left, right, condition.accept(shuttle),
-                    JoinRelType.INNER, ImmutableSet.<String>of(), false),
-                mapping));
+
+        final RelNode join = relBuilder.push(left)
+            .push(right)
+            .join(JoinRelType.INNER, condition.accept(shuttle))
+            .build();
+        relNodes.add(Pair.of(join, mapping));
       }
       if (pw != null) {
         pw.println(Util.last(relNodes));
@@ -266,11 +268,9 @@ public class MultiJoinOptimizeBushyRule extends RelOptRule {
     }
 
     final Pair<RelNode, Mappings.TargetMapping> top = Util.last(relNodes);
-    final RelNode project =
-        RelOptUtil.createProject(projectFactory, top.left,
-            Mappings.asList(top.right));
-
-    call.transformTo(project);
+    relBuilder.push(top.left)
+        .project(relBuilder.fields(top.right));
+    call.transformTo(relBuilder.build());
   }
 
   private void trace(List<Vertex> vertexes,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java
index 89ed15f..4537157 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMergeRule.java
@@ -24,6 +24,8 @@ import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.RelFactories.ProjectFactory;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.Permutation;
 
 import java.util.List;
@@ -35,15 +37,13 @@ import java.util.List;
  */
 public class ProjectMergeRule extends RelOptRule {
   public static final ProjectMergeRule INSTANCE =
-      new ProjectMergeRule(true, RelFactories.DEFAULT_PROJECT_FACTORY);
+      new ProjectMergeRule(true, RelFactories.LOGICAL_BUILDER);
 
   //~ Instance fields --------------------------------------------------------
 
   /** Whether to always merge projects. */
   private final boolean force;
 
-  private final ProjectFactory projectFactory;
-
   //~ Constructors -----------------------------------------------------------
 
   /**
@@ -51,13 +51,18 @@ public class ProjectMergeRule extends RelOptRule {
    *
    * @param force Whether to always merge projects
    */
-  public ProjectMergeRule(boolean force, ProjectFactory projectFactory) {
+  public ProjectMergeRule(boolean force, RelBuilderFactory relBuilderFactory) {
     super(
         operand(Project.class,
             operand(Project.class, any())),
+        relBuilderFactory,
         "ProjectMergeRule" + (force ? ":force_mode" : ""));
     this.force = force;
-    this.projectFactory = projectFactory;
+  }
+
+  @Deprecated // to be removed before 2.0
+  public ProjectMergeRule(boolean force, ProjectFactory projectFactory) {
+    this(force, RelBuilder.proto(projectFactory));
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -65,6 +70,7 @@ public class ProjectMergeRule extends RelOptRule {
   public void onMatch(RelOptRuleCall call) {
     final Project topProject = call.rel(0);
     final Project bottomProject = call.rel(1);
+    final RelBuilder relBuilder = call.builder();
 
     // If one or both projects are permutations, short-circuit the complex logic
     // of building a RexProgram.
@@ -81,10 +87,10 @@ public class ProjectMergeRule extends RelOptRule {
           return;
         }
         final Permutation product = topPermutation.product(bottomPermutation);
-        call.transformTo(
-            RelOptUtil.projectMapping(bottomProject.getInput(),
-                product.inverse(), topProject.getRowType().getFieldNames(),
-                projectFactory));
+        relBuilder.push(bottomProject.getInput());
+        relBuilder.project(relBuilder.fields(product),
+            topProject.getRowType().getFieldNames());
+        call.transformTo(relBuilder.build());
         return;
       }
     }
@@ -111,11 +117,9 @@ public class ProjectMergeRule extends RelOptRule {
     }
 
     // replace the two projects with a combined projection
-    RelNode newProjectRel = projectFactory.createProject(
-        bottomProject.getInput(), newProjects,
-        topProject.getRowType().getFieldNames());
-
-    call.transformTo(newProjectRel);
+    relBuilder.push(bottomProject.getInput());
+    relBuilder.project(newProjects, topProject.getRowType().getFieldNames());
+    call.transformTo(relBuilder.build());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java
index 065738e..0cf5d58 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectMultiJoinMergeRule.java
@@ -19,7 +19,10 @@ package org.apache.calcite.rel.rules;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
 
 /**
  * Planner rule that pushes
@@ -31,18 +34,15 @@ import org.apache.calcite.rel.logical.LogicalProject;
  */
 public class ProjectMultiJoinMergeRule extends RelOptRule {
   public static final ProjectMultiJoinMergeRule INSTANCE =
-      new ProjectMultiJoinMergeRule();
+      new ProjectMultiJoinMergeRule(RelFactories.LOGICAL_BUILDER);
 
   //~ Constructors -----------------------------------------------------------
 
-  /**
-   * Creates a ProjectMultiJoinMergeRule.
-   */
-  private ProjectMultiJoinMergeRule() {
+  /** Creates a ProjectMultiJoinMergeRule. */
+  private ProjectMultiJoinMergeRule(RelBuilderFactory relBuilderFactory) {
     super(
-        operand(
-            LogicalProject.class,
-            operand(MultiJoin.class, any())));
+        operand(LogicalProject.class,
+            operand(MultiJoin.class, any())), relBuilderFactory, null);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -66,15 +66,13 @@ public class ProjectMultiJoinMergeRule extends RelOptRule {
 
     // create a new MultiJoin that reflects the columns in the projection
     // above the MultiJoin
+    final RelBuilder relBuilder = call.builder();
     MultiJoin newMultiJoin =
         RelOptUtil.projectMultiJoin(multiJoin, project);
-    LogicalProject newProject =
-        (LogicalProject) RelOptUtil.createProject(
-            newMultiJoin,
-            project.getProjects(),
-            project.getRowType().getFieldNames());
+    relBuilder.push(newMultiJoin)
+        .project(project.getProjects(), project.getRowType().getFieldNames());
 
-    call.transformTo(newProject);
+    call.transformTo(relBuilder.build());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/ProjectSortTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ProjectSortTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ProjectSortTransposeRule.java
index fe53113..1287f84 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/ProjectSortTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/ProjectSortTransposeRule.java
@@ -19,14 +19,14 @@ package org.apache.calcite.rel.rules;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.Sort;
-import org.apache.calcite.rel.logical.LogicalProject;
 
 import com.google.common.collect.ImmutableList;
 
 /**
  * Planner rule that pushes
- * a {@link org.apache.calcite.rel.logical.LogicalProject}
+ * a {@link org.apache.calcite.rel.core.Project}
  * past a {@link org.apache.calcite.rel.core.Sort}.
  *
  * @see org.apache.calcite.rel.rules.SortProjectTransposeRule
@@ -42,15 +42,15 @@ public class ProjectSortTransposeRule extends RelOptRule {
    */
   private ProjectSortTransposeRule() {
     super(
-        operand(LogicalProject.class,
+        operand(Project.class,
             operand(Sort.class, any())));
   }
 
   //~ Methods ----------------------------------------------------------------
 
   public void onMatch(RelOptRuleCall call) {
-    LogicalProject project = call.rel(0);
-    Sort sort = call.rel(1);
+    final Project project = call.rel(0);
+    final Sort sort = call.rel(1);
     if (sort.getClass() != Sort.class) {
       return;
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/ProjectToWindowRule.java
----------------------------------------------------------------------
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 92fd282..fe6334e 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
@@ -21,13 +21,11 @@ import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptRuleOperand;
-import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.logical.LogicalCalc;
-import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rel.logical.LogicalWindow;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexDynamicParam;
@@ -40,6 +38,7 @@ import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexProgramBuilder;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.rex.RexWindow;
+import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
@@ -104,7 +103,8 @@ public abstract class ProjectToWindowRule extends RelOptRule {
         public void onMatch(RelOptRuleCall call) {
           Calc calc = call.rel(0);
           assert RexOver.containsOver(calc.getProgram());
-          CalcRelSplitter transform = new WindowedAggRelSplitter(calc);
+          final CalcRelSplitter transform =
+              new WindowedAggRelSplitter(calc, call.builder());
           RelNode newRel = transform.execute();
           call.transformTo(newRel);
         }
@@ -133,31 +133,30 @@ public abstract class ProjectToWindowRule extends RelOptRule {
                   project.getCluster().getRexBuilder());
           // temporary LogicalCalc, never registered
           final LogicalCalc calc = LogicalCalc.create(input, program);
-          CalcRelSplitter transform = new WindowedAggRelSplitter(calc) {
+          final CalcRelSplitter transform = new WindowedAggRelSplitter(calc,
+              call.builder()) {
             @Override protected RelNode handle(RelNode rel) {
-              if (rel instanceof LogicalCalc) {
-                LogicalCalc calc = (LogicalCalc) rel;
-                final RexProgram program = calc.getProgram();
-                rel = calc.getInput();
-                if (program.getCondition() != null) {
-                  rel = LogicalFilter.create(rel,
-                      program.expandLocalRef(
-                          program.getCondition()));
-                }
-                if (!program.projectsOnlyIdentity()) {
-                  rel = RelOptUtil.createProject(
-                      rel,
-                      Lists.transform(
-                          program.getProjectList(),
-                          new Function<RexLocalRef, RexNode>() {
-                            public RexNode apply(RexLocalRef a0) {
-                              return program.expandLocalRef(a0);
-                            }
-                          }),
-                      calc.getRowType().getFieldNames());
-                }
+              if (!(rel instanceof LogicalCalc)) {
+                return rel;
+              }
+              final LogicalCalc calc = (LogicalCalc) rel;
+              final RexProgram program = calc.getProgram();
+              relBuilder.push(calc.getInput());
+              if (program.getCondition() != null) {
+                relBuilder.filter(
+                    program.expandLocalRef(program.getCondition()));
+              }
+              if (!program.projectsOnlyIdentity()) {
+                relBuilder.project(
+                    Lists.transform(program.getProjectList(),
+                        new Function<RexLocalRef, RexNode>() {
+                          public RexNode apply(RexLocalRef a0) {
+                            return program.expandLocalRef(a0);
+                          }
+                        }),
+                    calc.getRowType().getFieldNames());
               }
-              return rel;
+              return relBuilder.build();
             }
           };
           RelNode newRel = transform.execute();
@@ -175,80 +174,69 @@ public abstract class ProjectToWindowRule extends RelOptRule {
   //~ Inner Classes ----------------------------------------------------------
 
   /**
-   * Splitter which distinguishes between windowed aggregation expressions
+   * Splitter that distinguishes between windowed aggregation expressions
    * (calls to {@link RexOver}) and ordinary expressions.
    */
   static class WindowedAggRelSplitter extends CalcRelSplitter {
-    WindowedAggRelSplitter(Calc calc) {
-      super(
-          calc,
-          new RelType[]{
-            new CalcRelSplitter.RelType("CalcRelType") {
-              protected boolean canImplement(RexFieldAccess field) {
-                return true;
-              }
+    private static final RelType[] REL_TYPES = {
+      new RelType("CalcRelType") {
+        protected boolean canImplement(RexFieldAccess field) {
+          return true;
+        }
 
-              protected boolean canImplement(RexDynamicParam param) {
-                return true;
-              }
+        protected boolean canImplement(RexDynamicParam param) {
+          return true;
+        }
 
-              protected boolean canImplement(RexLiteral literal) {
-                return true;
-              }
+        protected boolean canImplement(RexLiteral literal) {
+          return true;
+        }
 
-              protected boolean canImplement(RexCall call) {
-                return !(call instanceof RexOver);
-              }
+        protected boolean canImplement(RexCall call) {
+          return !(call instanceof RexOver);
+        }
 
-              protected RelNode makeRel(
-                  RelOptCluster cluster,
-                  RelTraitSet traits,
-                  RelNode child,
-                  RexProgram program) {
-                assert !program.containsAggs();
-                program = RexProgramBuilder.normalize(cluster.getRexBuilder(),
-                    program);
-                return super.makeRel(
-                    cluster,
-                    traits,
-                    child,
-                    program);
-              }
-            },
-            new CalcRelSplitter.RelType("WinAggRelType") {
-              protected boolean canImplement(RexFieldAccess field) {
-                return false;
-              }
+        protected RelNode makeRel(RelOptCluster cluster, RelTraitSet traitSet,
+            RelBuilder relBuilder, RelNode input, RexProgram program) {
+          assert !program.containsAggs();
+          program = RexProgramBuilder.normalize(cluster.getRexBuilder(),
+              program);
+          return super.makeRel(cluster, traitSet, relBuilder, input, program);
+        }
+      },
+      new RelType("WinAggRelType") {
+        protected boolean canImplement(RexFieldAccess field) {
+          return false;
+        }
 
-              protected boolean canImplement(RexDynamicParam param) {
-                return false;
-              }
+        protected boolean canImplement(RexDynamicParam param) {
+          return false;
+        }
 
-              protected boolean canImplement(RexLiteral literal) {
-                return false;
-              }
+        protected boolean canImplement(RexLiteral literal) {
+          return false;
+        }
 
-              protected boolean canImplement(RexCall call) {
-                return call instanceof RexOver;
-              }
+        protected boolean canImplement(RexCall call) {
+          return call instanceof RexOver;
+        }
 
-              protected boolean supportsCondition() {
-                return false;
-              }
+        protected boolean supportsCondition() {
+          return false;
+        }
 
-              protected RelNode makeRel(
-                  RelOptCluster cluster,
-                  RelTraitSet traits,
-                  RelNode child,
-                  RexProgram program) {
-                Util.permAssert(
-                    program.getCondition() == null,
-                    "WindowedAggregateRel cannot accept a condition");
-                return LogicalWindow.create(
-                    cluster, traits, child, program);
-              }
-            }
-          });
+        protected RelNode makeRel(RelOptCluster cluster, RelTraitSet traitSet,
+            RelBuilder relBuilder, RelNode input, RexProgram program) {
+          Util.permAssert(program.getCondition() == null,
+              "WindowedAggregateRel cannot accept a condition");
+          return LogicalWindow.create(cluster, traitSet, relBuilder, input,
+              program);
+        }
+      }
+    };
+
+    WindowedAggRelSplitter(Calc calc, RelBuilder relBuilder) {
+      super(calc, relBuilder, REL_TYPES);
     }
 
     @Override protected List<Set<Integer>> getCohorts() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinProjectTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinProjectTransposeRule.java
index 8cb450e..6e945e5 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinProjectTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SemiJoinProjectTransposeRule.java
@@ -18,10 +18,10 @@ package org.apache.calcite.rel.rules;
 
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.SemiJoin;
 import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rel.type.RelDataType;
@@ -32,6 +32,8 @@ import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexProgramBuilder;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Pair;
 
@@ -52,17 +54,18 @@ import java.util.List;
  */
 public class SemiJoinProjectTransposeRule extends RelOptRule {
   public static final SemiJoinProjectTransposeRule INSTANCE =
-      new SemiJoinProjectTransposeRule();
+      new SemiJoinProjectTransposeRule(RelFactories.LOGICAL_BUILDER);
 
   //~ Constructors -----------------------------------------------------------
 
   /**
    * Creates a SemiJoinProjectTransposeRule.
    */
-  private SemiJoinProjectTransposeRule() {
+  private SemiJoinProjectTransposeRule(RelBuilderFactory relBuilderFactory) {
     super(
         operand(SemiJoin.class,
-            some(operand(LogicalProject.class, any()))));
+            some(operand(LogicalProject.class, any()))),
+        relBuilderFactory, null);
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -71,9 +74,9 @@ public class SemiJoinProjectTransposeRule extends RelOptRule {
     SemiJoin semiJoin = call.rel(0);
     LogicalProject project = call.rel(1);
 
-    // convert the LHS semijoin keys to reference the child projection
+    // Convert the LHS semi-join keys to reference the child projection
     // expression; all projection expressions must be RexInputRefs,
-    // otherwise, we wouldn't have created this semijoin
+    // otherwise, we wouldn't have created this semi-join.
     final List<Integer> newLeftKeys = new ArrayList<>();
     final List<Integer> leftKeys = semiJoin.getLeftKeys();
     final List<RexNode> projExprs = project.getProjects();
@@ -93,13 +96,11 @@ public class SemiJoinProjectTransposeRule extends RelOptRule {
     // Create the new projection.  Note that the projection expressions
     // are the same as the original because they only reference the LHS
     // of the semijoin and the semijoin only projects out the LHS
-    RelNode newProject =
-        RelOptUtil.createProject(
-            newSemiJoin,
-            projExprs,
-            project.getRowType().getFieldNames());
+    final RelBuilder relBuilder = call.builder();
+    relBuilder.push(newSemiJoin);
+    relBuilder.project(projExprs, project.getRowType().getFieldNames());
 
-    call.transformTo(newProject);
+    call.transformTo(relBuilder.build());
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
index b6d87f5..4bc11ec 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SortJoinTransposeRule.java
@@ -36,9 +36,10 @@ import com.google.common.collect.ImmutableList;
 
 /**
  * Planner rule that pushes a {@link org.apache.calcite.rel.core.Sort} past a
- * {@link org.apache.calcite.rel.core.Join}. At the moment, we only consider
- * left/right outer joins.
- * However, an extesion for full outer joins for this rule could be envision.
+ * {@link org.apache.calcite.rel.core.Join}.
+ *
+ * <p>At the moment, we only consider left/right outer joins.
+ * However, an extension for full outer joins for this rule could be envisioned.
  * Special attention should be paid to null values for correctness issues.
  */
 public class SortJoinTransposeRule extends RelOptRule {
@@ -59,8 +60,7 @@ public class SortJoinTransposeRule extends RelOptRule {
 
   //~ Methods ----------------------------------------------------------------
 
-  @Override
-  public boolean matches(RelOptRuleCall call) {
+  @Override public boolean matches(RelOptRuleCall call) {
     final Sort sort = call.rel(0);
     final Join join = call.rel(1);
 
@@ -95,8 +95,7 @@ public class SortJoinTransposeRule extends RelOptRule {
     return true;
   }
 
-  @Override
-  public void onMatch(RelOptRuleCall call) {
+  @Override public void onMatch(RelOptRuleCall call) {
     final Sort sort = call.rel(0);
     final Join join = call.rel(1);
 
@@ -150,8 +149,7 @@ public class SortJoinTransposeRule extends RelOptRule {
     if (rowCount != null && fetch != null) {
       final int offsetVal = offset == null ? 0 : RexLiteral.intValue(offset);
       final int limit = RexLiteral.intValue(fetch);
-      final Double offsetLimit = new Double(offsetVal + limit);
-      if (offsetLimit < rowCount) {
+      if ((double) offsetVal + (double) limit < rowCount) {
         alreadySmaller = false;
       }
     }
@@ -159,3 +157,5 @@ public class SortJoinTransposeRule extends RelOptRule {
   }
 
 }
+
+// End SortJoinTransposeRule.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
index 2240c38..b86bbe3 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SortProjectTransposeRule.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rex.RexUtil;
@@ -37,7 +38,7 @@ import java.util.Map;
 /**
  * Planner rule that pushes
  * a {@link org.apache.calcite.rel.core.Sort}
- * past a {@link org.apache.calcite.rel.logical.LogicalProject}.
+ * past a {@link org.apache.calcite.rel.core.Project}.
  *
  * @see org.apache.calcite.rel.rules.ProjectSortTransposeRule
  */
@@ -52,17 +53,15 @@ public class SortProjectTransposeRule extends RelOptRule {
    */
   private SortProjectTransposeRule() {
     super(
-        operand(
-            Sort.class,
+        operand(Sort.class,
             operand(LogicalProject.class, any())));
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  // implement RelOptRule
   public void onMatch(RelOptRuleCall call) {
     final Sort sort = call.rel(0);
-    final LogicalProject project = call.rel(1);
+    final Project project = call.rel(1);
     final RelOptCluster cluster = project.getCluster();
 
     if (sort.getConvention() != project.getConvention()) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/UnionMergeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/UnionMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/UnionMergeRule.java
index 1d4cfcc..fbd3e76 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/UnionMergeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionMergeRule.java
@@ -22,12 +22,10 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.logical.LogicalUnion;
-import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
 import org.apache.calcite.util.Util;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * UnionMergeRule implements the rule for combining two
  * non-distinct {@link org.apache.calcite.rel.core.Union}s
@@ -35,38 +33,37 @@ import java.util.List;
  */
 public class UnionMergeRule extends RelOptRule {
   public static final UnionMergeRule INSTANCE =
-      new UnionMergeRule(LogicalUnion.class,
-          RelFactories.DEFAULT_SET_OP_FACTORY);
-
-  private final RelFactories.SetOpFactory setOpFactory;
+      new UnionMergeRule(LogicalUnion.class, RelFactories.LOGICAL_BUILDER);
 
   //~ Constructors -----------------------------------------------------------
 
-  /**
-   * Creates a UnionMergeRule.
-   */
-  public UnionMergeRule(Class<? extends Union> clazz,
-      RelFactories.SetOpFactory setOpFactory) {
+  /** Creates a UnionMergeRule. */
+  public UnionMergeRule(Class<? extends Union> unionClazz,
+      RelBuilderFactory relBuilderFactory) {
     super(
-        operand(
-            clazz,
+        operand(unionClazz,
             operand(RelNode.class, any()),
-            operand(RelNode.class, any())));
-    this.setOpFactory = setOpFactory;
+            operand(RelNode.class, any())),
+        relBuilderFactory, null);
+  }
+
+  @Deprecated // to be removed before 2.0
+  public UnionMergeRule(Class<? extends Union> unionClazz,
+      RelFactories.SetOpFactory setOpFactory) {
+    this(unionClazz, RelBuilder.proto(setOpFactory));
   }
 
   //~ Methods ----------------------------------------------------------------
 
-  // implement RelOptRule
   public void onMatch(RelOptRuleCall call) {
-    Union topUnion = call.rel(0);
-    Union bottomUnion;
+    final Union topUnion = call.rel(0);
 
     // We want to combine the Union that's in the second input first.
     // Hence, that's why the rule pattern matches on generic RelNodes
     // rather than explicit UnionRels.  By doing so, and firing this rule
     // in a bottom-up order, it allows us to only specify a single
     // pattern for this rule.
+    final Union bottomUnion;
     if (call.rel(2) instanceof Union) {
       bottomUnion = call.rel(2);
     } else if (call.rel(1) instanceof Union) {
@@ -82,22 +79,20 @@ public class UnionMergeRule extends RelOptRule {
 
     // Combine the inputs from the bottom union with the other inputs from
     // the top union
-    List<RelNode> unionInputs = new ArrayList<RelNode>();
+    final RelBuilder relBuilder = call.builder();
     if (call.rel(2) instanceof Union) {
       assert topUnion.getInputs().size() == 2;
-      unionInputs.add(topUnion.getInput(0));
-      unionInputs.addAll(bottomUnion.getInputs());
+      relBuilder.push(topUnion.getInput(0));
+      relBuilder.pushAll(bottomUnion.getInputs());
     } else {
-      unionInputs.addAll(bottomUnion.getInputs());
-      unionInputs.addAll(Util.skip(topUnion.getInputs()));
+      relBuilder.pushAll(bottomUnion.getInputs());
+      relBuilder.pushAll(Util.skip(topUnion.getInputs()));
     }
-    assert unionInputs.size()
-        == bottomUnion.getInputs().size()
+    int n = bottomUnion.getInputs().size()
         + topUnion.getInputs().size()
         - 1;
-    RelNode newUnion = setOpFactory.createSetOp(SqlKind.UNION,
-        unionInputs, true);
-    call.transformTo(newUnion);
+    relBuilder.union(true, n);
+    call.transformTo(relBuilder.build());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/rel/rules/UnionToDistinctRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/UnionToDistinctRule.java b/core/src/main/java/org/apache/calcite/rel/rules/UnionToDistinctRule.java
index 5bb9562..10ae1f4 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/UnionToDistinctRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/UnionToDistinctRule.java
@@ -18,12 +18,11 @@ package org.apache.calcite.rel.rules;
 
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.logical.LogicalUnion;
-import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
 
 /**
  * Planner rule that translates a distinct
@@ -35,32 +34,36 @@ import org.apache.calcite.sql.SqlKind;
  */
 public class UnionToDistinctRule extends RelOptRule {
   public static final UnionToDistinctRule INSTANCE =
-      new UnionToDistinctRule(LogicalUnion.class,
-          RelFactories.DEFAULT_SET_OP_FACTORY);
-
-  private final RelFactories.SetOpFactory setOpFactory;
+      new UnionToDistinctRule(LogicalUnion.class, RelFactories.LOGICAL_BUILDER);
 
   //~ Constructors -----------------------------------------------------------
 
   /**
    * Creates a UnionToDistinctRule.
    */
-  public UnionToDistinctRule(Class<? extends Union> clazz,
+  public UnionToDistinctRule(Class<? extends Union> unionClazz,
+      RelBuilderFactory relBuilderFactory) {
+    super(operand(unionClazz, any()), relBuilderFactory, null);
+  }
+
+  @Deprecated // to be removed before 2.0
+  public UnionToDistinctRule(Class<? extends Union> unionClazz,
       RelFactories.SetOpFactory setOpFactory) {
-    super(operand(clazz, any()));
-    this.setOpFactory = setOpFactory;
+    this(unionClazz, RelBuilder.proto(setOpFactory));
   }
 
   //~ Methods ----------------------------------------------------------------
 
   public void onMatch(RelOptRuleCall call) {
-    Union union = call.rel(0);
+    final Union union = call.rel(0);
     if (union.all) {
       return; // nothing to do
     }
-    RelNode unionAll = setOpFactory.createSetOp(SqlKind.UNION,
-        union.getInputs(), true);
-    call.transformTo(RelOptUtil.createDistinctRel(unionAll));
+    final RelBuilder relBuilder = call.builder();
+    relBuilder.pushAll(union.getInputs());
+    relBuilder.union(true, union.getInputs().size());
+    relBuilder.distinct();
+    call.transformTo(relBuilder.build());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
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 9e17837..0863332 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -36,7 +36,6 @@ import org.apache.calcite.rel.logical.LogicalTableFunctionScan;
 import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.logical.LogicalValues;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.rules.ProjectRemoveRule;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rel.type.RelDataTypeImpl;
@@ -47,6 +46,7 @@ import org.apache.calcite.rex.RexPermuteInputsShuttle;
 import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.rex.RexVisitor;
 import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
@@ -59,7 +59,6 @@ import org.apache.calcite.util.mapping.MappingType;
 import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.base.Function;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -99,13 +98,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
   //~ Instance fields --------------------------------------------------------
 
   private final ReflectUtil.MethodDispatcher<TrimResult> trimFieldsDispatcher;
-  private final RelFactories.ProjectFactory projectFactory;
-  private final RelFactories.FilterFactory filterFactory;
-  private final RelFactories.JoinFactory joinFactory;
-  private final RelFactories.SemiJoinFactory semiJoinFactory;
-  private final RelFactories.SortFactory sortFactory;
-  private final RelFactories.AggregateFactory aggregateFactory;
-  private final RelFactories.SetOpFactory setOpFactory;
+  private final RelBuilder relBuilder;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -114,23 +107,22 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
    *
    * @param validator Validator
    */
-  public RelFieldTrimmer(SqlValidator validator) {
-    this(validator,
-        RelFactories.DEFAULT_PROJECT_FACTORY,
-        RelFactories.DEFAULT_FILTER_FACTORY,
-        RelFactories.DEFAULT_JOIN_FACTORY,
-        RelFactories.DEFAULT_SEMI_JOIN_FACTORY,
-        RelFactories.DEFAULT_SORT_FACTORY,
-        RelFactories.DEFAULT_AGGREGATE_FACTORY,
-        RelFactories.DEFAULT_SET_OP_FACTORY);
+  public RelFieldTrimmer(SqlValidator validator, RelBuilder relBuilder) {
+    Util.discard(validator); // may be useful one day
+    this.relBuilder = relBuilder;
+    this.trimFieldsDispatcher =
+        ReflectUtil.createMethodDispatcher(
+            TrimResult.class,
+            this,
+            "trimFields",
+            RelNode.class,
+            ImmutableBitSet.class,
+            Set.class);
   }
 
-  /**
-   * Creates a RelFieldTrimmer.
-   *
-   * @param validator Validator
-   */
+  @Deprecated // to be removed before 2.0
   public RelFieldTrimmer(SqlValidator validator,
+      RelOptCluster cluster,
       RelFactories.ProjectFactory projectFactory,
       RelFactories.FilterFactory filterFactory,
       RelFactories.JoinFactory joinFactory,
@@ -138,22 +130,10 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
       RelFactories.SortFactory sortFactory,
       RelFactories.AggregateFactory aggregateFactory,
       RelFactories.SetOpFactory setOpFactory) {
-    Util.discard(validator); // may be useful one day
-    this.trimFieldsDispatcher =
-        ReflectUtil.createMethodDispatcher(
-            TrimResult.class,
-            this,
-            "trimFields",
-            RelNode.class,
-            ImmutableBitSet.class,
-            Set.class);
-    this.projectFactory = Preconditions.checkNotNull(projectFactory);
-    this.filterFactory = Preconditions.checkNotNull(filterFactory);
-    this.joinFactory = Preconditions.checkNotNull(joinFactory);
-    this.semiJoinFactory = Preconditions.checkNotNull(semiJoinFactory);
-    this.sortFactory = Preconditions.checkNotNull(sortFactory);
-    this.aggregateFactory = Preconditions.checkNotNull(aggregateFactory);
-    this.setOpFactory = Preconditions.checkNotNull(setOpFactory);
+    this(validator,
+        RelBuilder.proto(projectFactory, filterFactory, joinFactory,
+            semiJoinFactory, sortFactory, aggregateFactory, setOpFactory)
+        .create(cluster, null));
   }
 
   //~ Methods ----------------------------------------------------------------
@@ -245,11 +225,10 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
               ? rexBuilder.makeZeroLiteral(field.getType())
               : rexBuilder.makeInputRef(field.getType(), source));
     }
-    RelNode project =
-        projectFactory.createProject(
-            trimResult.left, exprList, nameList);
+    relBuilder.push(trimResult.left)
+        .project(exprList, nameList);
     return new TrimResult(
-        project,
+        relBuilder.build(),
         Mappings.createIdentity(fieldList.size()));
   }
 
@@ -383,17 +362,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
         RelOptUtil.permute(project.getCluster().getTypeFactory(), rowType,
             mapping);
 
-    final RelNode newProject;
-    if (ProjectRemoveRule.isIdentity(newProjects, newInput.getRowType())) {
-      // The new project would be the identity. It is equivalent to return
-      // its child.
-      newProject = newInput;
-    } else {
-      newProject = projectFactory.createProject(newInput, newProjects,
-          newRowType.getFieldNames());
-      assert newProject.getClass() == project.getClass();
-    }
-    return new TrimResult(newProject, mapping);
+    relBuilder.push(newInput);
+    relBuilder.project(newProjects, newRowType.getFieldNames());
+    return new TrimResult(relBuilder.build(), mapping);
   }
 
   /** Creates a project with a dummy column, to protect the parts of the system
@@ -414,9 +385,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     }
     final RexLiteral expr =
         cluster.getRexBuilder().makeExactLiteral(BigDecimal.ZERO);
-    final RelNode newProject = projectFactory.createProject(input,
-        ImmutableList.<RexNode>of(expr), ImmutableList.of("DUMMY"));
-    return new TrimResult(newProject, mapping);
+    relBuilder.push(input);
+    relBuilder.project(ImmutableList.<RexNode>of(expr), ImmutableList.of("DUMMY"));
+    return new TrimResult(relBuilder.build(), mapping);
   }
 
   /**
@@ -463,13 +434,13 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     RexNode newConditionExpr =
         conditionExpr.accept(shuttle);
 
-    final RelNode newFilter = filterFactory.createFilter(
-        newInput, newConditionExpr);
+    relBuilder.push(newInput);
+    relBuilder.filter(newConditionExpr);
 
     // The result has the same mapping as the input gave us. Sometimes we
     // return fields that the consumer didn't ask for, because the filter
     // needs them for its condition.
-    return new TrimResult(newFilter, inputMapping);
+    return new TrimResult(relBuilder.build(), inputMapping);
   }
 
   /**
@@ -510,15 +481,19 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
           Mappings.createIdentity(fieldCount));
     }
 
-    final RelCollation newCollation =
-        sort.getTraitSet().canonize(RexUtil.apply(inputMapping, collation));
-    final RelNode newSort =
-        sortFactory.createSort(newInput, newCollation, sort.offset, sort.fetch);
+    relBuilder.push(newInput);
+    final int offset =
+        sort.offset == null ? 0 : RexLiteral.intValue(sort.offset);
+    final int fetch =
+        sort.fetch == null ? -1 : RexLiteral.intValue(sort.fetch);
+    final ImmutableList<RexNode> fields =
+        relBuilder.fields(RexUtil.apply(inputMapping, collation));
+    relBuilder.sortLimit(offset, fetch, fields);
 
     // The result has the same mapping as the input gave us. Sometimes we
     // return fields that the consumer didn't ask for, because the filter
     // needs them for its condition.
-    return new TrimResult(newSort, inputMapping);
+    return new TrimResult(relBuilder.build(), inputMapping);
   }
 
   /**
@@ -636,10 +611,11 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     RexNode newConditionExpr =
         conditionExpr.accept(shuttle);
 
-    final RelNode newJoin;
+    relBuilder.push(newInputs.get(0));
+    relBuilder.push(newInputs.get(1));
+
     if (join instanceof SemiJoin) {
-      newJoin = semiJoinFactory.createSemiJoin(newInputs.get(0),
-          newInputs.get(1), newConditionExpr);
+      relBuilder.semiJoin(newConditionExpr);
       // For SemiJoins only map fields from the left-side
       Mapping inputMapping = inputMappings.get(0);
       mapping = Mappings.create(MappingType.INVERSE_SURJECTION,
@@ -654,12 +630,10 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
         mapping.set(pair.source + offset, pair.target + newOffset);
       }
     } else {
-      newJoin = joinFactory.createJoin(newInputs.get(0), newInputs.get(1),
-          newConditionExpr, join.getJoinType(), join.getVariablesStopped(),
-          join.isSemiJoinDone());
+      relBuilder.join(join.getJoinType(), newConditionExpr);
     }
 
-    return new TrimResult(newJoin, mapping);
+    return new TrimResult(relBuilder.build(), mapping);
   }
 
   /**
@@ -686,12 +660,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     final Mapping mapping = createMapping(fieldsUsed, fieldCount);
 
     // Create input with trimmed columns.
-    final List<RelNode> newInputs = new ArrayList<>();
     for (RelNode input : setOp.getInputs()) {
       TrimResult trimResult =
           trimChild(setOp, input, fieldsUsed, extraFields);
-      RelNode newInput = trimResult.left;
-      final Mapping inputMapping = trimResult.right;
 
       // We want "mapping", the input gave us "inputMapping", compute
       // "remaining" mapping.
@@ -704,30 +675,44 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
       // the consumer asked for mapping = [b, d],
       // and the transformed input has columns inputMapping = [d, a, b].
       // remaining will permute [b, d] to [d, a, b].
-      Mapping remaining = Mappings.divide(mapping, inputMapping);
+      Mapping remaining = Mappings.divide(mapping, trimResult.right);
 
       // Create a projection; does nothing if remaining is identity.
-      newInput = RelOptUtil.projectMapping(newInput, remaining, null,
-          projectFactory);
+      relBuilder.push(trimResult.left);
+      relBuilder.permute(remaining);
 
-      if (input != newInput) {
+      if (input != relBuilder.peek()) {
         ++changeCount;
       }
-      newInputs.add(newInput);
     }
 
     // If the input is unchanged, and we need to project all columns,
     // there's to do.
     if (changeCount == 0
         && mapping.isIdentity()) {
+      for (RelNode input : setOp.getInputs()) {
+        relBuilder.build();
+      }
       return new TrimResult(
           setOp,
           mapping);
     }
 
-    RelNode newSetOp =
-        setOpFactory.createSetOp(setOp.kind, newInputs, setOp.all);
-    return new TrimResult(newSetOp, mapping);
+    switch (setOp.kind) {
+    case UNION:
+      relBuilder.union(setOp.all, setOp.getInputs().size());
+      break;
+    case INTERSECT:
+      relBuilder.intersect(setOp.all, setOp.getInputs().size());
+      break;
+    case EXCEPT:
+      assert setOp.getInputs().size() == 2;
+      relBuilder.minus(setOp.all);
+      break;
+    default:
+      throw new AssertionError("unknown setOp " + setOp);
+    }
+    return new TrimResult(relBuilder.build(), mapping);
   }
 
   /**
@@ -831,28 +816,29 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     }
 
     // Now create new agg calls, and populate mapping for them.
-    final List<AggregateCall> newAggCallList = new ArrayList<>();
+    relBuilder.push(newInput);
+    final List<RelBuilder.AggCall> newAggCallList = new ArrayList<>();
     j = groupCount + indicatorCount;
     for (AggregateCall aggCall : aggregate.getAggCallList()) {
       if (fieldsUsed.get(j)) {
-        AggregateCall newAggCall =
-            aggCall.copy(Mappings.apply2(inputMapping, aggCall.getArgList()),
-                Mappings.apply(inputMapping, aggCall.filterArg));
-        if (newAggCall.equals(aggCall)) {
-          newAggCall = aggCall; // immutable -> canonize to save space
-        }
+        final ImmutableList<RexNode> args =
+            relBuilder.fields(
+                Mappings.apply2(inputMapping, aggCall.getArgList()));
+        final RexNode filterArg = aggCall.filterArg < 0 ? null
+            : relBuilder.field(Mappings.apply(inputMapping, aggCall.filterArg));
+        RelBuilder.AggCall newAggCall =
+            relBuilder.aggregateCall(aggCall.getAggregation(),
+                aggCall.isDistinct(), filterArg, aggCall.name, args);
         mapping.set(j, groupCount + indicatorCount + newAggCallList.size());
         newAggCallList.add(newAggCall);
       }
       ++j;
     }
 
-    RelNode newAggregate = aggregateFactory.createAggregate(newInput,
-        aggregate.indicator, newGroupSet, newGroupSets, newAggCallList);
-
-    assert newAggregate.getClass() == aggregate.getClass();
+    final RelBuilder.GroupKey groupKey = relBuilder.groupKey(newGroupSet, newGroupSets);
+    relBuilder.aggregate(groupKey, newAggCallList);
 
-    return new TrimResult(newAggregate, mapping);
+    return new TrimResult(relBuilder.build(), mapping);
   }
 
   /**
@@ -1015,7 +1001,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
           (RelNode) tableAccessRel, fieldsUsed, extraFields);
     }
     final RelNode newTableAccessRel =
-        tableAccessRel.project(fieldsUsed, extraFields, this.projectFactory);
+        tableAccessRel.project(fieldsUsed, extraFields, relBuilder);
 
     // Some parts of the system can't handle rows with zero fields, so
     // pretend that one field is used.

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
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 b51a0dc..c6c03b2 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -142,6 +142,7 @@ import org.apache.calcite.sql.validate.SqlValidatorImpl;
 import org.apache.calcite.sql.validate.SqlValidatorNamespace;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.tools.RelBuilder;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.NlsString;
@@ -543,7 +544,9 @@ public class SqlToRelConverter {
    * @return Field trimmer
    */
   protected RelFieldTrimmer newFieldTrimmer() {
-    return new RelFieldTrimmer(validator);
+    final RelBuilder relBuilder =
+        RelFactories.LOGICAL_BUILDER.create(cluster, null);
+    return new RelFieldTrimmer(validator, relBuilder);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
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 696e22c..34eb219 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -20,12 +20,14 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.plan.Context;
 import org.apache.calcite.plan.Contexts;
 import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptSchema;
 import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.Project;
@@ -33,6 +35,7 @@ import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.rules.ProjectRemoveRule;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
@@ -50,10 +53,13 @@ import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.NlsString;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Stacks;
 import org.apache.calcite.util.Util;
+import org.apache.calcite.util.mapping.Mapping;
+import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
@@ -64,7 +70,11 @@ import com.google.common.collect.Lists;
 import java.math.BigDecimal;
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * Builder for relational expressions.
@@ -98,6 +108,7 @@ public class RelBuilder {
   private final RelFactories.SortFactory sortFactory;
   private final RelFactories.SetOpFactory setOpFactory;
   private final RelFactories.JoinFactory joinFactory;
+  private final RelFactories.SemiJoinFactory semiJoinFactory;
   private final RelFactories.ValuesFactory valuesFactory;
   private final RelFactories.TableScanFactory scanFactory;
   private final List<Frame> stack = new ArrayList<>();
@@ -127,6 +138,9 @@ public class RelBuilder {
     this.joinFactory =
         Util.first(context.unwrap(RelFactories.JoinFactory.class),
             RelFactories.DEFAULT_JOIN_FACTORY);
+    this.semiJoinFactory =
+        Util.first(context.unwrap(RelFactories.SemiJoinFactory.class),
+            RelFactories.DEFAULT_SEMI_JOIN_FACTORY);
     this.valuesFactory =
         Util.first(context.unwrap(RelFactories.ValuesFactory.class),
             RelFactories.DEFAULT_VALUES_FACTORY);
@@ -156,16 +170,21 @@ public class RelBuilder {
     return cluster.getTypeFactory();
   }
 
-  /** Creates a {@link ProtoRelBuilder}, a partially-created RelBuilder.
+  /** Creates a {@link RelBuilderFactory}, a partially-created RelBuilder.
    * Just add a {@link RelOptCluster} and a {@link RelOptSchema} */
-  public static ProtoRelBuilder proto(final Context context) {
-    return new ProtoRelBuilder() {
+  public static RelBuilderFactory proto(final Context context) {
+    return new RelBuilderFactory() {
       public RelBuilder create(RelOptCluster cluster, RelOptSchema schema) {
         return new RelBuilder(context, cluster, schema);
       }
     };
   }
 
+  /** Creates a {@link RelBuilderFactory} that uses a given set of factories. */
+  public static RelBuilderFactory proto(Object... factories) {
+    return proto(Contexts.of(factories));
+  }
+
   // Methods for manipulating the stack
 
   /** Adds a relational expression to be the input to the next relational
@@ -180,6 +199,14 @@ public class RelBuilder {
     return this;
   }
 
+  /** Pushes a collection of relational expressions. */
+  public RelBuilder pushAll(Iterable<? extends RelNode> nodes) {
+    for (RelNode node : nodes) {
+      push(node);
+    }
+    return this;
+  }
+
   /** Returns the final relational expression.
    *
    * <p>Throws if the stack is empty.
@@ -328,6 +355,52 @@ public class RelBuilder {
     return nodes.build();
   }
 
+  /** Returns references to fields for a given collation. */
+  public ImmutableList<RexNode> fields(RelCollation collation) {
+    final ImmutableList.Builder<RexNode> nodes = ImmutableList.builder();
+    for (RelFieldCollation fieldCollation : collation.getFieldCollations()) {
+      RexNode node = field(fieldCollation.getFieldIndex());
+      switch (fieldCollation.direction) {
+      case DESCENDING:
+        node = desc(node);
+      }
+      switch (fieldCollation.nullDirection) {
+      case FIRST:
+        node = nullsFirst(node);
+        break;
+      case LAST:
+        node = nullsLast(node);
+        break;
+      }
+      nodes.add(node);
+    }
+    return nodes.build();
+  }
+
+  /** Returns references to fields for a given list of input ordinals. */
+  public ImmutableList<RexNode> fields(List<? extends Number> ordinals) {
+    final ImmutableList.Builder<RexNode> nodes = ImmutableList.builder();
+    for (Number ordinal : ordinals) {
+      RexNode node = field(ordinal.intValue());
+      nodes.add(node);
+    }
+    return nodes.build();
+  }
+
+  /** Returns references to fields identified by name. */
+  public ImmutableList<RexNode> fields(Iterable<String> fieldNames) {
+    final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
+    for (String fieldName : fieldNames) {
+      builder.add(field(fieldName));
+    }
+    return builder.build();
+  }
+
+  /** Returns references to fields identified by a mapping. */
+  public ImmutableList<RexNode> fields(Mappings.TargetMapping mapping) {
+    return fields(Mappings.asList(mapping));
+  }
+
   /** Creates a call to a scalar operator. */
   public RexNode call(SqlOperator operator, RexNode... operands) {
     final RexBuilder builder = cluster.getRexBuilder();
@@ -448,69 +521,101 @@ public class RelBuilder {
 
   /** Creates a group key. */
   public GroupKey groupKey(Iterable<? extends RexNode> nodes) {
-    return new GroupKeyImpl(ImmutableList.copyOf(nodes));
+    return new GroupKeyImpl(ImmutableList.copyOf(nodes), null);
+  }
+
+  /** Creates a group key with grouping sets. */
+  public GroupKey groupKey(Iterable<? extends RexNode> nodes,
+      Iterable<? extends Iterable<? extends RexNode>> nodeLists) {
+    final ImmutableList.Builder<ImmutableList<RexNode>> builder =
+        ImmutableList.builder();
+    for (Iterable<? extends RexNode> nodeList : nodeLists) {
+      builder.add(ImmutableList.copyOf(nodeList));
+    }
+    return new GroupKeyImpl(ImmutableList.copyOf(nodes), builder.build());
   }
 
   /** Creates a group key of fields identified by ordinal. */
   public GroupKey groupKey(int... fieldOrdinals) {
-    final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
-    for (int fieldOrdinal : fieldOrdinals) {
-      builder.add(field(fieldOrdinal));
-    }
-    return groupKey(builder.build());
+    return groupKey(fields(ImmutableIntList.of(fieldOrdinals)));
   }
 
   /** Creates a group key of fields identified by name. */
   public GroupKey groupKey(String... fieldNames) {
-    final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
-    for (String fieldName : fieldNames) {
-      builder.add(field(fieldName));
+    return groupKey(fields(ImmutableList.copyOf(fieldNames)));
+  }
+
+  /** Creates a group key with grouping sets, both identified by field positions
+   * in the underlying relational expression.
+   *
+   * <p>This method of creating a group key does not allow you to group on new
+   * expressions, only column projections, but is efficient, especially when you
+   * are coming from an existing {@link Aggregate}. */
+  public GroupKey groupKey(ImmutableBitSet groupSet,
+      ImmutableList<ImmutableBitSet> groupSets) {
+    if (groupSet.length() > peek().getRowType().getFieldCount()) {
+      throw new IllegalArgumentException("out of bounds: " + groupSet);
+    }
+    if (groupSets == null) {
+      groupSets = ImmutableList.of(groupSet);
     }
-    return groupKey(builder.build());
+    final ImmutableList<RexNode> nodes =
+        fields(ImmutableIntList.of(groupSet.toArray()));
+    final List<ImmutableList<RexNode>> nodeLists =
+        Lists.transform(groupSets,
+            new Function<ImmutableBitSet, ImmutableList<RexNode>>() {
+              public ImmutableList<RexNode> apply(ImmutableBitSet input) {
+                return fields(ImmutableIntList.of(input.toArray()));
+              }
+            });
+    return groupKey(nodes, nodeLists);
   }
 
   /** Creates a call to an aggregate function. */
-  public AggCall aggregateCall(SqlAggFunction aggFunction,
-      boolean distinct, String alias, RexNode... operands) {
-    return aggregateCall(aggFunction, distinct, alias,
+  public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+      RexNode filter, String alias, RexNode... operands) {
+    return aggregateCall(aggFunction, distinct, filter, alias,
         ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to an aggregate function. */
-  public AggCall aggregateCall(SqlAggFunction aggFunction,
-      boolean distinct, String alias, Iterable<? extends RexNode> operands) {
-    return new AggCallImpl(aggFunction, distinct, alias,
+  public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+      RexNode filter, String alias, Iterable<? extends RexNode> operands) {
+    return new AggCallImpl(aggFunction, distinct, filter, alias,
         ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to the COUNT aggregate function. */
   public AggCall count(boolean distinct, String alias, RexNode... operands) {
-    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, alias, operands);
+    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, null, alias,
+        operands);
   }
 
   /** Creates a call to the COUNT(*) aggregate function. */
   public AggCall countStar(String alias) {
-    return aggregateCall(SqlStdOperatorTable.COUNT, false, alias);
+    return aggregateCall(SqlStdOperatorTable.COUNT, false, null, alias);
   }
 
   /** Creates a call to the SUM aggregate function. */
   public AggCall sum(boolean distinct, String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.SUM, distinct, alias, operand);
+    return aggregateCall(SqlStdOperatorTable.SUM, distinct, null, alias,
+        operand);
   }
 
   /** Creates a call to the AVG aggregate function. */
   public AggCall avg(boolean distinct, String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.AVG, distinct, alias, operand);
+    return aggregateCall(
+        SqlStdOperatorTable.AVG, distinct, null, alias, operand);
   }
 
   /** Creates a call to the MIN aggregate function. */
   public AggCall min(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MIN, false, alias, operand);
+    return aggregateCall(SqlStdOperatorTable.MIN, false, null, alias, operand);
   }
 
   /** Creates a call to the MAX aggregate function. */
   public AggCall max(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MAX, false, alias, operand);
+    return aggregateCall(SqlStdOperatorTable.MAX, false, null, alias, operand);
   }
 
   // Methods that create relational expressions
@@ -562,21 +667,49 @@ public class RelBuilder {
   /** Creates a {@link org.apache.calcite.rel.core.Project} of the given list
    * of expressions.
    *
-   * <p>Infers all field names.
-   * If an expression projects an input field,
-   * or is a cast an input field,
-   * uses the input field name.
-   * If an expression is a call to
-   * {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#AS}
-   * (see {@link #alias}), removes the
-   * call but uses the intended alias.
-   * After the field names have been inferred, makes the
-   * field names unique by appending numeric suffixes. */
+   * <p>Infers names as would {@link #project(Iterable, Iterable)} if all
+   * suggested names were null.
+   *
+   * @param nodes Expressions
+   */
   public RelBuilder project(Iterable<? extends RexNode> nodes) {
+    return project(nodes, ImmutableList.<String>of());
+  }
+
+  /** Creates a {@link org.apache.calcite.rel.core.Project} of the given list
+   * of expressions, using the given names.
+   *
+   * <p>Names are deduced as follows:
+   * <ul>
+   *   <li>If the length of {@code fieldNames} is greater than the index of
+   *     the current entry in {@code nodes}, and the entry in
+   *     {@code fieldNames} is not null, uses it; otherwise
+   *   <li>If an expression projects an input field,
+   *     or is a cast an input field,
+   *     uses the input field name; otherwise
+   *   <li>If an expression is a call to
+   *     {@link org.apache.calcite.sql.fun.SqlStdOperatorTable#AS}
+   *     (see {@link #alias}), removes the call but uses the intended alias.
+   * </ul>
+   *
+   * <p>After the field names have been inferred, makes the
+   * field names unique by appending numeric suffixes.
+   *
+   * @param nodes Expressions
+   * @param fieldNames Suggested field names
+   */
+  public RelBuilder project(Iterable<? extends RexNode> nodes,
+      Iterable<String> fieldNames) {
     final List<String> names = new ArrayList<>();
     final List<RexNode> exprList = Lists.newArrayList(nodes);
+    final Iterator<String> nameIterator = fieldNames.iterator();
     for (RexNode node : nodes) {
-      names.add(inferAlias(exprList, node));
+      final String name = nameIterator.hasNext() ? nameIterator.next() : null;
+      final String name2 = inferAlias(exprList, node);
+      names.add(Util.first(name, name2));
+    }
+    if (ProjectRemoveRule.isIdentity(exprList, peek().getRowType())) {
+      return this;
     }
     final RelNode project =
         projectFactory.createProject(build(), ImmutableList.copyOf(exprList),
@@ -634,17 +767,41 @@ public class RelBuilder {
   /** Creates an {@link org.apache.calcite.rel.core.Aggregate} with a list of
    * calls. */
   public RelBuilder aggregate(GroupKey groupKey, Iterable<AggCall> aggCalls) {
-    final ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
     final RelDataType inputRowType = peek().getRowType();
     final List<RexNode> extraNodes = projects(inputRowType);
-    for (RexNode node : ((GroupKeyImpl) groupKey).nodes) {
-      builder.set(registerExpression(extraNodes, node));
+    final GroupKeyImpl groupKey_ = (GroupKeyImpl) groupKey;
+    final ImmutableBitSet groupSet =
+        ImmutableBitSet.of(registerExpressions(extraNodes, groupKey_.nodes));
+    final ImmutableList<ImmutableBitSet> groupSets;
+    if (groupKey_.nodeLists != null) {
+      final int sizeBefore = extraNodes.size();
+      final SortedSet<ImmutableBitSet> groupSetSet =
+          new TreeSet<>(ImmutableBitSet.ORDERING);
+      for (ImmutableList<RexNode> nodeList : groupKey_.nodeLists) {
+        final ImmutableBitSet groupSet2 =
+            ImmutableBitSet.of(registerExpressions(extraNodes, nodeList));
+        if (!groupSet.contains(groupSet2)) {
+          throw new IllegalArgumentException("group set element " + nodeList
+              + " must be a subset of group key");
+        }
+        groupSetSet.add(groupSet2);
+      }
+      groupSets = ImmutableList.copyOf(groupSetSet);
+      if (extraNodes.size() > sizeBefore) {
+        throw new IllegalArgumentException(
+            "group sets contained expressions not in group key: "
+                + extraNodes.subList(sizeBefore, extraNodes.size()));
+      }
+    } else {
+      groupSets = ImmutableList.of(groupSet);
     }
-    final ImmutableBitSet groupSet = builder.build();
     for (AggCall aggCall : aggCalls) {
-      final AggCallImpl aggCall1 = (AggCallImpl) aggCall;
-      for (RexNode operand : aggCall1.operands) {
-        registerExpression(extraNodes, operand);
+      if (aggCall instanceof AggCallImpl) {
+        final AggCallImpl aggCall1 = (AggCallImpl) aggCall;
+        registerExpressions(extraNodes, aggCall1.operands);
+        if (aggCall1.filter != null) {
+          registerExpression(extraNodes, aggCall1.filter);
+        }
       }
     }
     if (extraNodes.size() > inputRowType.getFieldCount()) {
@@ -653,18 +810,27 @@ public class RelBuilder {
     final RelNode r = build();
     final List<AggregateCall> aggregateCalls = new ArrayList<>();
     for (AggCall aggCall : aggCalls) {
-      final List<Integer> args = new ArrayList<>();
-      final AggCallImpl aggCall1 = (AggCallImpl) aggCall;
-      for (RexNode operand : aggCall1.operands) {
-        args.add(registerExpression(extraNodes, operand));
+      final AggregateCall aggregateCall;
+      if (aggCall instanceof AggCallImpl) {
+        final AggCallImpl aggCall1 = (AggCallImpl) aggCall;
+        final List<Integer> args = registerExpressions(extraNodes, aggCall1.operands);
+        final int filterArg = aggCall1.filter == null ? -1
+            : registerExpression(extraNodes, aggCall1.filter);
+        aggregateCall =
+            AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct, args,
+                filterArg, groupSet.cardinality(), r, null, aggCall1.alias);
+      } else {
+        aggregateCall = ((AggCallImpl2) aggCall).aggregateCall;
       }
-      aggregateCalls.add(
-          AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct,
-              args, -1, groupSet.cardinality(), r, null, aggCall1.alias));
+      aggregateCalls.add(aggregateCall);
     }
 
-    RelNode aggregate = aggregateFactory.createAggregate(r, false, groupSet,
-        ImmutableList.of(groupSet), aggregateCalls);
+    assert ImmutableBitSet.ORDERING.isStrictlyOrdered(groupSets) : groupSets;
+    for (ImmutableBitSet set : groupSets) {
+      assert groupSet.contains(set);
+    }
+    RelNode aggregate = aggregateFactory.createAggregate(r,
+        groupSets.size() > 1, groupSet, groupSets, aggregateCalls);
     push(aggregate);
     return this;
   }
@@ -687,18 +853,60 @@ public class RelBuilder {
     return i;
   }
 
+  private static List<Integer> registerExpressions(List<RexNode> extraNodes,
+      Iterable<? extends RexNode> nodes) {
+    final List<Integer> builder = new ArrayList<>();
+    for (RexNode node : nodes) {
+      builder.add(registerExpression(extraNodes, node));
+    }
+    return builder;
+  }
+
+  private RelBuilder setOp(boolean all, SqlKind kind, int n) {
+    List<RelNode> inputs = new LinkedList<>();
+    for (int i = 0; i < n; i++) {
+      inputs.add(0, build());
+    }
+    switch (kind) {
+    case UNION:
+    case INTERSECT:
+      if (n < 1) {
+        throw new IllegalArgumentException("bad INTERSECT/UNION input count");
+      }
+      break;
+    case EXCEPT:
+      if (n != 2) {
+        throw new AssertionError("bad EXCEPT input count");
+      }
+      break;
+    default:
+      throw new AssertionError("bad setOp " + kind);
+    }
+    switch (n) {
+    case 1:
+      return push(inputs.get(0));
+    default:
+      return push(setOpFactory.createSetOp(kind, inputs, all));
+    }
+  }
+
   /** Creates a {@link org.apache.calcite.rel.core.Union} of the two most recent
    * relational expressions on the stack.
    *
    * @param all Whether to create UNION ALL
    */
   public RelBuilder union(boolean all) {
-    final RelNode left = build();
-    final RelNode right = build();
-    final RelNode union = setOpFactory.createSetOp(SqlKind.UNION,
-        ImmutableList.of(left, right), all);
-    push(union);
-    return this;
+    return union(all, 2);
+  }
+
+  /** Creates a {@link org.apache.calcite.rel.core.Union} of the {@code n}
+   * most recent relational expressions on the stack.
+   *
+   * @param all Whether to create UNION ALL
+   * @param n Number of inputs to the UNION operator
+   */
+  public RelBuilder union(boolean all, int n) {
+    return setOp(all, SqlKind.UNION, n);
   }
 
   /** Creates an {@link org.apache.calcite.rel.core.Intersect} of the two most
@@ -707,12 +915,17 @@ public class RelBuilder {
    * @param all Whether to create INTERSECT ALL
    */
   public RelBuilder intersect(boolean all) {
-    final RelNode left = build();
-    final RelNode right = build();
-    final RelNode intersect = setOpFactory.createSetOp(SqlKind.INTERSECT,
-        ImmutableList.of(left, right), all);
-    push(intersect);
-    return this;
+    return intersect(all, 2);
+  }
+
+  /** Creates an {@link org.apache.calcite.rel.core.Intersect} of the {@code n}
+   * most recent relational expressions on the stack.
+   *
+   * @param all Whether to create INTERSECT ALL
+   * @param n Number of inputs to the INTERSECT operator
+   */
+  public RelBuilder intersect(boolean all, int n) {
+    return setOp(all, SqlKind.INTERSECT, n);
   }
 
   /** Creates a {@link org.apache.calcite.rel.core.Minus} of the two most recent
@@ -721,19 +934,23 @@ public class RelBuilder {
    * @param all Whether to create EXCEPT ALL
    */
   public RelBuilder minus(boolean all) {
-    final RelNode left = build();
-    final RelNode right = build();
-    final RelNode except = setOpFactory.createSetOp(SqlKind.EXCEPT,
-        ImmutableList.of(left, right), all);
-    push(except);
-    return this;
+    return setOp(all, SqlKind.EXCEPT, 2);
   }
 
   /** Creates a {@link org.apache.calcite.rel.core.Join}. */
-  public RelBuilder join(JoinRelType joinType, RexNode condition) {
+  public RelBuilder join(JoinRelType joinType, RexNode condition0,
+      RexNode... conditions) {
+    return join(joinType, Lists.asList(condition0, conditions));
+  }
+
+  /** Creates a {@link org.apache.calcite.rel.core.Join} with multiple
+   * conditions. */
+  public RelBuilder join(JoinRelType joinType,
+      Iterable<? extends RexNode> conditions) {
     final Frame right = Stacks.pop(stack);
     final Frame left = Stacks.pop(stack);
-    final RelNode join = joinFactory.createJoin(left.rel, right.rel, condition,
+    final RelNode join = joinFactory.createJoin(left.rel, right.rel,
+        RexUtil.composeConjunction(cluster.getRexBuilder(), conditions, false),
         joinType, ImmutableSet.<String>of(), false);
     final List<Pair<String, RelDataType>> pairs = new ArrayList<>();
     pairs.addAll(left.right);
@@ -759,9 +976,24 @@ public class RelBuilder {
               field(2, 0, fieldName),
               field(2, 1, fieldName)));
     }
-    final RexNode condition =
-        RexUtil.composeConjunction(cluster.getRexBuilder(), conditions, false);
-    return join(joinType, condition);
+    return join(joinType, conditions);
+  }
+
+  /** Creates a {@link org.apache.calcite.rel.core.SemiJoin}. */
+  public RelBuilder semiJoin(Iterable<? extends RexNode> conditions) {
+    final Frame right = Stacks.pop(stack);
+    final Frame left = Stacks.pop(stack);
+    final RelNode semiJoin =
+        semiJoinFactory.createSemiJoin(left.rel, right.rel,
+            RexUtil.composeConjunction(cluster.getRexBuilder(), conditions,
+                false));
+    Stacks.push(stack, new Frame(semiJoin, left.right));
+    return this;
+  }
+
+  /** Creates a {@link org.apache.calcite.rel.core.SemiJoin}. */
+  public RelBuilder semiJoin(RexNode... conditions) {
+    return semiJoin(ImmutableList.copyOf(conditions));
   }
 
   /** Assigns a table alias to the top entry on the stack. */
@@ -1007,6 +1239,46 @@ public class RelBuilder {
     }
   }
 
+  /**
+   * Creates a projection that converts the current relational expression's
+   * output to a desired row type.
+   *
+   * @param castRowType row type after cast
+   * @param rename      if true, use field names from castRowType; if false,
+   *                    preserve field names from rel
+   */
+  public RelBuilder convert(RelDataType castRowType, boolean rename) {
+    final RelNode r = build();
+    final RelNode r2 =
+        RelOptUtil.createCastRel(r, castRowType, rename, projectFactory);
+    push(r2);
+    return this;
+  }
+
+  public RelBuilder permute(Mapping mapping) {
+    assert mapping.getMappingType().isSingleSource();
+    assert mapping.getMappingType().isMandatorySource();
+    if (mapping.isIdentity()) {
+      return this;
+    }
+    final List<RexNode> exprList = Lists.newArrayList();
+    for (int i = 0; i < mapping.getTargetCount(); i++) {
+      exprList.add(field(mapping.getSource(i)));
+    }
+    return project(exprList);
+  }
+
+  public RelBuilder aggregate(GroupKey groupKey,
+      List<AggregateCall> aggregateCalls) {
+    return aggregate(groupKey,
+        Lists.transform(
+            aggregateCalls, new Function<AggregateCall, AggCall>() {
+              public AggCall apply(AggregateCall input) {
+                return new AggCallImpl2(input);
+              }
+            }));
+  }
+
   /** Information necessary to create a call to an aggregate function.
    *
    * @see RelBuilder#aggregateCall */
@@ -1021,10 +1293,13 @@ public class RelBuilder {
 
   /** Implementation of {@link RelBuilder.GroupKey}. */
   private static class GroupKeyImpl implements GroupKey {
-    private final ImmutableList<RexNode> nodes;
+    final ImmutableList<RexNode> nodes;
+    final ImmutableList<ImmutableList<RexNode>> nodeLists;
 
-    GroupKeyImpl(ImmutableList<RexNode> nodes) {
-      this.nodes = nodes;
+    GroupKeyImpl(ImmutableList<RexNode> nodes,
+        ImmutableList<ImmutableList<RexNode>> nodeLists) {
+      this.nodes = Preconditions.checkNotNull(nodes);
+      this.nodeLists = nodeLists;
     }
   }
 
@@ -1032,32 +1307,28 @@ public class RelBuilder {
   private static class AggCallImpl implements AggCall {
     private final SqlAggFunction aggFunction;
     private final boolean distinct;
+    private final RexNode filter;
     private final String alias;
     private final ImmutableList<RexNode> operands;
 
-    AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
+    AggCallImpl(SqlAggFunction aggFunction, boolean distinct, RexNode filter,
         String alias, ImmutableList<RexNode> operands) {
       this.aggFunction = aggFunction;
       this.distinct = distinct;
+      this.filter = filter;
       this.alias = alias;
       this.operands = operands;
     }
   }
 
-  /** A partially-created RelBuilder.
-   *
-   * <p>Add a cluster, and optionally a schema,
-   * when you want to create a builder.
-   *
-   * <p>A {@code ProtoRelBuilder} can be shared among queries, and thus can
-   * be inside a {@link RelOptRule}. It is a nice way to encapsulate the policy
-   * that this particular rule instance should create {@code DrillFilter}
-   * and {@code DrillProject} versus {@code HiveFilter} and {@code HiveProject}.
-   *
-   * @see RelFactories#DEFAULT_PROTO
-   */
-  public interface ProtoRelBuilder {
-    RelBuilder create(RelOptCluster cluster, RelOptSchema schema);
+  /** Implementation of {@link RelBuilder.AggCall} that wraps an
+   * {@link AggregateCall}. */
+  private static class AggCallImpl2 implements AggCall {
+    private final AggregateCall aggregateCall;
+
+    AggCallImpl2(AggregateCall aggregateCall) {
+      this.aggregateCall = Preconditions.checkNotNull(aggregateCall);
+    }
   }
 
   /** Builder stack frame.
@@ -1079,8 +1350,10 @@ public class RelBuilder {
 
     private static String deriveAlias(RelNode rel) {
       if (rel instanceof TableScan) {
-        TableScan scan = (TableScan) rel;
-        return Util.last(scan.getTable().getQualifiedName());
+        final List<String> names = rel.getTable().getQualifiedName();
+        if (!names.isEmpty()) {
+          return Util.last(names);
+        }
       }
       return null;
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java b/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java
new file mode 100644
index 0000000..2aefca6
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilderFactory.java
@@ -0,0 +1,41 @@
+/*
+ * 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.tools;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptSchema;
+import org.apache.calcite.rel.core.RelFactories;
+
+/** A partially-created RelBuilder.
+ *
+ * <p>Add a cluster, and optionally a schema,
+ * when you want to create a builder.
+ *
+ * <p>A {@code ProtoRelBuilder} can be shared among queries, and thus can
+ * be inside a {@link RelOptRule}. It is a nice way to encapsulate the policy
+ * that this particular rule instance should create {@code DrillFilter}
+ * and {@code DrillProject} versus {@code HiveFilter} and {@code HiveProject}.
+ *
+ * @see RelFactories#LOGICAL_BUILDER
+ */
+public interface RelBuilderFactory {
+  /** Creates a RelBuilder. */
+  RelBuilder create(RelOptCluster cluster, RelOptSchema schema);
+}
+
+// End RelBuilderFactory.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/815fa262/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
index cc7ec8c..265990e 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcAdapterTest.java
@@ -206,7 +206,7 @@ public class JdbcAdapterTest {
             + "FROM \"SCOTT\".\"EMP\") AS \"t0\" ON \"t\".\"MGR\" = \"t0\".\"EMPNO\" AND (\"t\".\"SAL\" > \"t0\".\"SAL\" OR \"t\".\"HIREDATE\" < \"t0\".\"HIREDATE\")");
   }
 
-  @Test public void tesJoin3TablesPlan() {
+  @Test public void testJoin3TablesPlan() {
     CalciteAssert.model(JdbcTest.SCOTT_MODEL)
         .query("select  empno, ename, dname, grade \n"
             + "from scott.emp e inner join scott.dept d \n"