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/02 02:09:49 UTC

[13/18] incubator-calcite git commit: [CALCITE-819] Add RelRoot, a contract for the result of a relational expression

[CALCITE-819] Add RelRoot, a contract for the result of a relational expression

Remove PRESERVE collation


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/5a397063
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/5a397063
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/5a397063

Branch: refs/heads/master
Commit: 5a3970635030ddde27e4237da5d8c5c3419c34b5
Parents: 0c1a135
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Aug 13 01:00:09 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Sep 1 16:17:16 2015 -0700

----------------------------------------------------------------------
 .../adapter/enumerable/EnumerableProject.java   |   3 +-
 .../enumerable/EnumerableProjectRule.java       |  15 +-
 .../apache/calcite/interpreter/ProjectNode.java |   2 +-
 .../org/apache/calcite/jdbc/CalcitePrepare.java |  11 +-
 .../org/apache/calcite/materialize/Lattice.java |   2 +-
 .../org/apache/calcite/plan/RelOptTable.java    |   5 +-
 .../org/apache/calcite/plan/RelOptUtil.java     |   7 +-
 .../calcite/prepare/CalciteMaterializer.java    |   2 +-
 .../calcite/prepare/CalcitePrepareImpl.java     | 102 ++++---
 .../calcite/prepare/LixToRelTranslator.java     |   3 +-
 .../org/apache/calcite/prepare/PlannerImpl.java |  29 +-
 .../org/apache/calcite/prepare/Prepare.java     | 103 +++----
 .../calcite/rel/RelCollationTraitDef.java       |   6 +-
 .../org/apache/calcite/rel/RelCollations.java   |   1 +
 .../java/org/apache/calcite/rel/RelInput.java   |   6 +
 .../java/org/apache/calcite/rel/RelRoot.java    | 171 ++++++++++++
 .../java/org/apache/calcite/rel/SingleRel.java  |   2 +-
 .../calcite/rel/externalize/RelJsonReader.java  |  40 ++-
 .../rel/rules/AggregateStarTableRule.java       |   4 +-
 .../java/org/apache/calcite/rex/RexUtil.java    |   4 -
 .../apache/calcite/schema/impl/ViewTable.java   |  16 +-
 .../sql2rel/RelStructuredTypeFlattener.java     |  13 +-
 .../calcite/sql2rel/SqlToRelConverter.java      | 274 ++++++++++---------
 .../java/org/apache/calcite/tools/Planner.java  |   5 +
 .../java/org/apache/calcite/tools/Programs.java |  13 +-
 .../java/org/apache/calcite/tools/RuleSets.java |   3 +-
 .../apache/calcite/util/ImmutableIntList.java   |   6 +-
 .../apache/calcite/test/InterpreterTest.java    |  13 +
 .../calcite/test/JdbcFrontLinqBackTest.java     |  13 +-
 .../java/org/apache/calcite/test/JdbcTest.java  |   8 +-
 .../apache/calcite/test/RelMetadataTest.java    |   9 +-
 .../apache/calcite/test/RelOptRulesTest.java    |  28 +-
 .../org/apache/calcite/test/RelOptTestBase.java |   4 +-
 .../apache/calcite/test/RexTransformerTest.java |   6 +-
 .../calcite/test/SqlToRelConverterTest.java     |   2 +-
 .../apache/calcite/test/SqlToRelTestBase.java   |  19 +-
 .../org/apache/calcite/tools/PlannerTest.java   |  29 +-
 .../calcite/test/SqlToRelConverterTest.xml      |  15 +-
 core/src/test/resources/sql/sort.oq             |  13 +
 39 files changed, 617 insertions(+), 390 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
index 9ab0fb2..e6852f7 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProject.java
@@ -63,7 +63,8 @@ public class EnumerableProject extends Project implements EnumerableRel {
     Util.discard(flags);
   }
 
-  /** Creates a LogicalProject, specifying row type rather than field names. */
+  /** Creates an EnumerableProject, specifying row type rather than field
+   * names. */
   public static EnumerableProject create(final RelNode input,
       final List<? extends RexNode> projects, RelDataType rowType) {
     final RelOptCluster cluster = input.getCluster();

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectRule.java
index 459e025..3f998da 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectRule.java
@@ -18,8 +18,6 @@ package org.apache.calcite.adapter.enumerable;
 
 import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.rel.RelCollationTraitDef;
-import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.logical.LogicalProject;
@@ -36,18 +34,7 @@ class EnumerableProjectRule extends ConverterRule {
 
   public RelNode convert(RelNode rel) {
     final LogicalProject project = (LogicalProject) rel;
-    if (rel.getTraitSet().getTrait(RelCollationTraitDef.INSTANCE)
-        != RelCollations.PRESERVE) {
-      return EnumerableProject.create(
-          convert(project.getInput(),
-              project.getInput().getTraitSet()
-                  .replace(EnumerableConvention.INSTANCE)),
-          project.getProjects(),
-          project.getRowType());
-    }
-    // Special case for PRESERVE, to hand-create collation.
-    return new EnumerableProject(rel.getCluster(),
-        rel.getTraitSet().replace(EnumerableConvention.INSTANCE),
+    return EnumerableProject.create(
         convert(project.getInput(),
             project.getInput().getTraitSet()
                 .replace(EnumerableConvention.INSTANCE)),

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java b/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java
index 576bf6f..4c280b7 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java
@@ -20,7 +20,7 @@ import org.apache.calcite.rel.core.Project;
 
 /**
  * Interpreter node that implements a
- * {@link org.apache.calcite.rel.logical.LogicalFilter}.
+ * {@link org.apache.calcite.rel.core.Project}.
  */
 public class ProjectNode extends AbstractSingleNode<Project> {
   private final Scalar scalar;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
index fe2f898..ae5fba4 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalcitePrepare.java
@@ -32,6 +32,7 @@ import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexNode;
@@ -226,12 +227,12 @@ public interface CalcitePrepare {
   /** The result of parsing and validating a SQL query and converting it to
    * relational algebra. */
   class ConvertResult extends ParseResult {
-    public final RelNode relNode;
+    public final RelRoot root;
 
     public ConvertResult(CalcitePrepareImpl prepare, SqlValidator validator,
-        String sql, SqlNode sqlNode, RelDataType rowType, RelNode relNode) {
+        String sql, SqlNode sqlNode, RelDataType rowType, RelRoot root) {
       super(prepare, validator, sql, sqlNode, rowType);
-      this.relNode = relNode;
+      this.root = root;
     }
   }
 
@@ -245,10 +246,10 @@ public interface CalcitePrepare {
 
     public AnalyzeViewResult(CalcitePrepareImpl prepare,
         SqlValidator validator, String sql, SqlNode sqlNode,
-        RelDataType rowType, RelNode relNode, Table table,
+        RelDataType rowType, RelRoot root, Table table,
         ImmutableList<String> tablePath, RexNode constraint,
         ImmutableIntList columnMapping) {
-      super(prepare, validator, sql, sqlNode, rowType, relNode);
+      super(prepare, validator, sql, sqlNode, rowType, root);
       this.table = table;
       this.tablePath = tablePath;
       this.constraint = constraint;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/materialize/Lattice.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/materialize/Lattice.java b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
index c2b07c3..ee7fef6 100644
--- a/core/src/main/java/org/apache/calcite/materialize/Lattice.java
+++ b/core/src/main/java/org/apache/calcite/materialize/Lattice.java
@@ -584,7 +584,7 @@ public class Lattice {
       // Walk the join tree.
       List<RelNode> relNodes = Lists.newArrayList();
       List<int[][]> tempLinks = Lists.newArrayList();
-      populate(relNodes, tempLinks, parsed.relNode);
+      populate(relNodes, tempLinks, parsed.root.rel);
 
       // Get aliases.
       List<String> aliases = Lists.newArrayList();

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptTable.java b/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
index 8f34ed4..56d01d7 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptTable.java
@@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
@@ -112,9 +113,7 @@ public interface RelOptTable {
 
   /** Can expand a view into relational expressions. */
   interface ViewExpander {
-    RelNode expandView(
-        RelDataType rowType,
-        String queryString,
+    RelRoot expandView(RelDataType rowType, String queryString,
         List<String> schemaPath);
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
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 76af520..7762bf7 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -19,6 +19,7 @@ package org.apache.calcite.plan;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.rel.RelHomogeneousShuttle;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.RelVisitor;
 import org.apache.calcite.rel.RelWriter;
@@ -385,6 +386,7 @@ public abstract class RelOptUtil {
           AggregateCall.create(minFunction,
               false,
               ImmutableList.of(0),
+              -1,
               0,
               ret,
               null,
@@ -462,6 +464,7 @@ public abstract class RelOptUtil {
           AggregateCall.create(minFunction,
               false,
               ImmutableList.of(projectedKeyCount),
+              -1,
               projectedKeyCount,
               ret,
               null,
@@ -2566,9 +2569,7 @@ public abstract class RelOptUtil {
         return cluster;
       }
 
-      public RelNode expandView(
-          RelDataType rowType,
-          String queryString,
+      public RelRoot expandView(RelDataType rowType, String queryString,
           List<String> schemaPath) {
         throw new UnsupportedOperationException();
       }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java b/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java
index 901dcb8..36f621e 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalciteMaterializer.java
@@ -81,7 +81,7 @@ class CalciteMaterializer extends CalcitePrepareImpl.CalcitePreparingStmt {
         getSqlToRelConverter(getSqlValidator(), catalogReader);
 
     materialization.queryRel =
-        sqlToRelConverter2.convertQuery(node, true, true);
+        sqlToRelConverter2.convertQuery(node, true, true).rel;
 
     // Identify and substitute a StarTable in queryRel.
     //

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 524e34f..3f9293c 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -18,6 +18,7 @@ package org.apache.calcite.prepare;
 
 import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.enumerable.EnumerableBindable;
+import org.apache.calcite.adapter.enumerable.EnumerableCalc;
 import org.apache.calcite.adapter.enumerable.EnumerableConvention;
 import org.apache.calcite.adapter.enumerable.EnumerableInterpretable;
 import org.apache.calcite.adapter.enumerable.EnumerableInterpreterRule;
@@ -64,7 +65,9 @@ import org.apache.calcite.plan.hep.HepProgramBuilder;
 import org.apache.calcite.plan.volcano.VolcanoPlanner;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationTraitDef;
+import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.TableScan;
@@ -94,6 +97,7 @@ import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.runtime.Bindable;
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.runtime.Typed;
@@ -117,6 +121,7 @@ import org.apache.calcite.sql2rel.SqlToRelConverter;
 import org.apache.calcite.sql2rel.StandardConvertletTable;
 import org.apache.calcite.tools.Frameworks;
 import org.apache.calcite.util.ImmutableIntList;
+import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
@@ -293,17 +298,18 @@ public class CalcitePrepareImpl implements CalcitePrepare {
     if (analyze) {
       converter.enableTableAccessConversion(false);
     }
-    final RelNode relNode = converter.convertQuery(sqlNode1, false, true);
+    final RelRoot root = converter.convertQuery(sqlNode1, false, true);
     if (analyze) {
-      return analyze_(validator, sql, sqlNode1, relNode, fail);
+      return analyze_(validator, sql, sqlNode1, root, fail);
     }
     return new ConvertResult(this, validator, sql, sqlNode1,
-        validator.getValidatedNodeType(sqlNode1), relNode);
+        validator.getValidatedNodeType(sqlNode1), root);
   }
 
   private AnalyzeViewResult analyze_(SqlValidator validator, String sql,
-      SqlNode sqlNode, RelNode rel, boolean fail) {
-    final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+      SqlNode sqlNode, RelRoot root, boolean fail) {
+    final RexBuilder rexBuilder = root.rel.getCluster().getRexBuilder();
+    RelNode rel = root.rel;
     final RelNode viewRel = rel;
     Project project;
     if (rel instanceof Project) {
@@ -331,7 +337,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
             RESOURCE.modifiableViewMustBeBasedOnSingleTable());
       }
       return new AnalyzeViewResult(this, validator, sql, sqlNode,
-          validator.getValidatedNodeType(sqlNode), rel, null, null, null,
+          validator.getValidatedNodeType(sqlNode), root, null, null, null,
           null);
     }
     final RelOptTable targetRelTable = scan.getTable();
@@ -357,7 +363,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
                       Util.last(tablePath)));
             }
             return new AnalyzeViewResult(this, validator, sql, sqlNode,
-                validator.getValidatedNodeType(sqlNode), rel, null, null, null,
+                validator.getValidatedNodeType(sqlNode), root, null, null, null,
                 null);
           }
           projectMap.put(index, rexBuilder.makeInputRef(viewRel, node.i));
@@ -396,12 +402,12 @@ public class CalcitePrepareImpl implements CalcitePrepare {
                 Util.last(tablePath)));
       }
       return new AnalyzeViewResult(this, validator, sql, sqlNode,
-          validator.getValidatedNodeType(sqlNode), rel, null, null, null,
+          validator.getValidatedNodeType(sqlNode), root, null, null, null,
           null);
     }
 
     return new AnalyzeViewResult(this, validator, sql, sqlNode,
-        validator.getValidatedNodeType(sqlNode), rel, table,
+        validator.getValidatedNodeType(sqlNode), root, table,
         ImmutableList.copyOf(tablePath),
         constraint, ImmutableIntList.copyOf(columnMapping));
   }
@@ -594,7 +600,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
         getColumnMetaDataList(typeFactory, x, x, origins);
     final Meta.CursorFactory cursorFactory =
         Meta.CursorFactory.deduce(columns, null);
-    return new CalciteSignature<T>(
+    return new CalciteSignature<>(
         sql,
         ImmutableList.<AvaticaParameter>of(),
         ImmutableMap.<String, Object>of(),
@@ -688,7 +694,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
           preparingStmt.prepareQueryable(queryable, x);
     }
 
-    final List<AvaticaParameter> parameters = new ArrayList<AvaticaParameter>();
+    final List<AvaticaParameter> parameters = new ArrayList<>();
     final RelDataType parameterRowType = preparedResult.getParameterRowType();
     for (RelDataTypeField field : parameterRowType.getFieldList()) {
       RelDataType type = field.getType();
@@ -732,7 +738,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
   private List<ColumnMetaData> getColumnMetaDataList(
       JavaTypeFactory typeFactory, RelDataType x, RelDataType jdbcType,
       List<List<String>> originList) {
-    final List<ColumnMetaData> columns = new ArrayList<ColumnMetaData>();
+    final List<ColumnMetaData> columns = new ArrayList<>();
     for (Ord<RelDataTypeField> pair : Ord.zip(jdbcType.getFieldList())) {
       final RelDataTypeField field = pair.e;
       final RelDataType type = field.getType();
@@ -931,9 +937,15 @@ public class CalcitePrepareImpl implements CalcitePrepare {
 
       final RelOptCluster cluster = prepare.createCluster(planner, rexBuilder);
 
-      RelNode rootRel =
+      final RelNode rel =
           new LixToRelTranslator(cluster, CalcitePreparingStmt.this)
               .translate(queryable);
+      final RelDataType rowType = rel.getRowType();
+      final List<Pair<Integer, String>> fields =
+          Pair.zip(ImmutableIntList.identity(rowType.getFieldCount()),
+              rowType.getFieldNames());
+      RelRoot root = new RelRoot(rel, resultType, SqlKind.SELECT, fields,
+          RelCollations.EMPTY);
 
       if (timingTracer != null) {
         timingTracer.traceTime("end sql2rel");
@@ -946,23 +958,20 @@ public class CalcitePrepareImpl implements CalcitePrepare {
 
       // Structured type flattening, view expansion, and plugging in
       // physical storage.
-      rootRel = flattenTypes(rootRel, true);
+      root = root.withRel(flattenTypes(root.rel, true));
 
       // Trim unused fields.
-      rootRel = trimUnusedFields(rootRel);
+      root = trimUnusedFields(root);
 
       final List<Materialization> materializations = ImmutableList.of();
       final List<CalciteSchema.LatticeEntry> lattices = ImmutableList.of();
-      rootRel = optimize(rootRel, materializations, lattices);
+      root = optimize(root, materializations, lattices);
 
       if (timingTracer != null) {
         timingTracer.traceTime("end optimization");
       }
 
-      return implement(
-          resultType,
-          rootRel,
-          SqlKind.SELECT);
+      return implement(root);
     }
 
     @Override protected SqlToRelConverter getSqlToRelConverter(
@@ -991,9 +1000,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
       return sqlToRelConverter.decorrelate(query, rootRel);
     }
 
-    @Override public RelNode expandView(
-        RelDataType rowType,
-        String queryString,
+    @Override public RelRoot expandView(RelDataType rowType, String queryString,
         List<String> schemaPath) {
       expansionDepth++;
 
@@ -1012,11 +1019,11 @@ public class CalcitePrepareImpl implements CalcitePrepare {
 
       SqlToRelConverter sqlToRelConverter =
           getSqlToRelConverter(validator, catalogReader);
-      RelNode relNode =
+      RelRoot root =
           sqlToRelConverter.convertQuery(sqlNode1, true, false);
 
       --expansionDepth;
-      return relNode;
+      return root;
     }
 
     private SqlValidatorImpl createSqlValidator(CatalogReader catalogReader) {
@@ -1035,25 +1042,34 @@ public class CalcitePrepareImpl implements CalcitePrepare {
     @Override protected PreparedResult createPreparedExplanation(
         RelDataType resultType,
         RelDataType parameterRowType,
-        RelNode rootRel,
+        RelRoot root,
         boolean explainAsXml,
         SqlExplainLevel detailLevel) {
       return new CalcitePreparedExplain(
-          resultType, parameterRowType, rootRel, explainAsXml, detailLevel);
+          resultType, parameterRowType, root, explainAsXml, detailLevel);
     }
 
-    @Override protected PreparedResult implement(
-        RelDataType rowType,
-        RelNode rootRel,
-        SqlKind sqlKind) {
-      RelDataType resultType = rootRel.getRowType();
-      boolean isDml = sqlKind.belongsTo(SqlKind.DML);
+    @Override protected PreparedResult implement(RelRoot root) {
+      RelDataType resultType = root.rel.getRowType();
+      boolean isDml = root.kind.belongsTo(SqlKind.DML);
       final Bindable bindable;
       if (resultConvention == BindableConvention.INSTANCE) {
-        bindable = Interpreters.bindable(rootRel);
+        bindable = Interpreters.bindable(root.rel);
       } else {
+        EnumerableRel enumerable = (EnumerableRel) root.rel;
+        if (!root.isRefTrivial()) {
+          final List<RexNode> projects = new ArrayList<>();
+          final RexBuilder rexBuilder = enumerable.getCluster().getRexBuilder();
+          for (int field : Pair.left(root.fields)) {
+            projects.add(rexBuilder.makeInputRef(enumerable, field));
+          }
+          RexProgram program = RexProgram.create(enumerable.getRowType(),
+              projects, null, root.validatedRowType, rexBuilder);
+          enumerable = EnumerableCalc.create(enumerable, program);
+        }
+
         bindable = EnumerableInterpretable.toBindable(internalParameters,
-            context.spark(), (EnumerableRel) rootRel, prefer);
+            context.spark(), enumerable, prefer);
       }
 
       if (timingTracer != null) {
@@ -1068,9 +1084,11 @@ public class CalcitePrepareImpl implements CalcitePrepare {
           resultType,
           parameterRowType,
           fieldOrigins,
-          ImmutableList.copyOf(collations),
-          rootRel,
-          mapTableModOp(isDml, sqlKind),
+          root.collation.getFieldCollations().isEmpty()
+              ? ImmutableList.<RelCollation>of()
+              : ImmutableList.of(root.collation),
+          root.rel,
+          mapTableModOp(isDml, root.kind),
           isDml) {
         public String getCode() {
           throw new UnsupportedOperationException();
@@ -1092,10 +1110,10 @@ public class CalcitePrepareImpl implements CalcitePrepare {
     public CalcitePreparedExplain(
         RelDataType resultType,
         RelDataType parameterRowType,
-        RelNode rootRel,
+        RelRoot root,
         boolean explainAsXml,
         SqlExplainLevel detailLevel) {
-      super(resultType, parameterRowType, rootRel, explainAsXml, detailLevel);
+      super(resultType, parameterRowType, root, explainAsXml, detailLevel);
     }
 
     public Bindable getBindable() {
@@ -1131,7 +1149,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
 
     public List<RexNode> toRexList(BlockStatement statement) {
       final List<Expression> simpleList = simpleList(statement);
-      final List<RexNode> list = new ArrayList<RexNode>();
+      final List<RexNode> list = new ArrayList<>();
       for (Expression expression1 : simpleList) {
         list.add(toRex(expression1));
       }
@@ -1216,7 +1234,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
     }
 
     private List<RexNode> toRex(List<Expression> expressions) {
-      ArrayList<RexNode> list = new ArrayList<RexNode>();
+      final List<RexNode> list = new ArrayList<>();
       for (Expression expression : expressions) {
         list.add(toRex(expression));
       }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java b/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java
index ed1671c..4f6a94f 100644
--- a/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java
+++ b/core/src/main/java/org/apache/calcite/prepare/LixToRelTranslator.java
@@ -28,6 +28,7 @@ import org.apache.calcite.linq4j.tree.Types;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rel.logical.LogicalProject;
 import org.apache.calcite.rel.logical.LogicalTableScan;
@@ -63,7 +64,7 @@ class LixToRelTranslator implements RelOptTable.ToRelContext {
     return cluster;
   }
 
-  public RelNode expandView(RelDataType rowType, String queryString,
+  public RelRoot expandView(RelDataType rowType, String queryString,
       List<String> schemaPath) {
     return preparingStmt.expandView(rowType, queryString, schemaPath);
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
index 1c67178..d58cfd8 100644
--- a/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/PlannerImpl.java
@@ -25,6 +25,7 @@ import org.apache.calcite.plan.RelOptTable.ViewExpander;
 import org.apache.calcite.plan.RelTraitDef;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.schema.SchemaPlus;
@@ -75,7 +76,7 @@ public class PlannerImpl implements Planner {
   private SqlNode validatedSqlNode;
 
   // set in STATE_5_CONVERT
-  private RelNode rel;
+  private RelRoot root;
 
   /** Creates a planner. Not a public API; call
    * {@link org.apache.calcite.tools.Frameworks#getPlanner} instead. */
@@ -178,7 +179,11 @@ public class PlannerImpl implements Planner {
     return validatedSqlNode;
   }
 
-  public RelNode convert(SqlNode sql) throws RelConversionException {
+  public final RelNode convert(SqlNode sql) throws RelConversionException {
+    return rel(sql).rel;
+  }
+
+  public RelRoot rel(SqlNode sql) throws RelConversionException {
     ensure(State.STATE_4_VALIDATED);
     assert validatedSqlNode != null;
     final RexBuilder rexBuilder = createRexBuilder();
@@ -188,17 +193,18 @@ public class PlannerImpl implements Planner {
             createCatalogReader(), cluster, convertletTable);
     sqlToRelConverter.setTrimUnusedFields(false);
     sqlToRelConverter.enableTableAccessConversion(false);
-    rel = sqlToRelConverter.convertQuery(validatedSqlNode, false, true);
-    rel = sqlToRelConverter.flattenTypes(rel, true);
-    rel = RelDecorrelator.decorrelateQuery(rel);
+    root =
+        sqlToRelConverter.convertQuery(validatedSqlNode, false, true);
+    root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true));
+    root = root.withRel(RelDecorrelator.decorrelateQuery(root.rel));
     state = State.STATE_5_CONVERTED;
-    return rel;
+    return root;
   }
 
   /** Implements {@link org.apache.calcite.plan.RelOptTable.ViewExpander}
    * interface for {@link org.apache.calcite.tools.Planner}. */
   public class ViewExpanderImpl implements ViewExpander {
-    public RelNode expandView(RelDataType rowType, String queryString,
+    public RelRoot expandView(RelDataType rowType, String queryString,
         List<String> schemaPath) {
       SqlParser parser = SqlParser.create(queryString, parserConfig);
       SqlNode sqlNode;
@@ -224,12 +230,11 @@ public class PlannerImpl implements Planner {
       sqlToRelConverter.setTrimUnusedFields(false);
       sqlToRelConverter.enableTableAccessConversion(false);
 
-      RelNode rel =
-          sqlToRelConverter.convertQuery(validatedSqlNode, true, false);
-      rel = sqlToRelConverter.flattenTypes(rel, true);
-      rel = RelDecorrelator.decorrelateQuery(rel);
+      root = sqlToRelConverter.convertQuery(validatedSqlNode, true, false);
+      root = root.withRel(sqlToRelConverter.flattenTypes(root.rel, true));
+      root = root.withRel(RelDecorrelator.decorrelateQuery(root.rel));
 
-      return rel;
+      return PlannerImpl.this.root;
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/prepare/Prepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/Prepare.java b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
index 403ad76..43ae738 100644
--- a/core/src/main/java/org/apache/calcite/prepare/Prepare.java
+++ b/core/src/main/java/org/apache/calcite/prepare/Prepare.java
@@ -30,7 +30,7 @@ import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.logical.LogicalTableModify;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexExecutorImpl;
@@ -57,7 +57,6 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
 import java.lang.reflect.Type;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.logging.Level;
@@ -79,8 +78,6 @@ public abstract class Prepare {
   protected final Convention resultConvention;
   protected CalciteTimingTracer timingTracer;
   protected List<List<String>> fieldOrigins;
-  protected final List<RelCollation> collations = new ArrayList<>();
-  protected boolean ordered;
   protected RelDataType parameterRowType;
 
   // temporary. for testing.
@@ -102,26 +99,26 @@ public abstract class Prepare {
   protected abstract PreparedResult createPreparedExplanation(
       RelDataType resultType,
       RelDataType parameterRowType,
-      RelNode rootRel,
+      RelRoot root,
       boolean explainAsXml,
       SqlExplainLevel detailLevel);
 
   /**
    * Optimizes a query plan.
    *
-   * @param rootRel root of a relational expression
+   * @param root Root of relational expression tree
    * @param materializations Tables known to be populated with a given query
    * @param lattices Lattices
    * @return an equivalent optimized relational expression
    */
-  protected RelNode optimize(final RelNode rootRel,
+  protected RelRoot optimize(final RelRoot root,
       final List<Materialization> materializations,
       final List<CalciteSchema.LatticeEntry> lattices) {
-    final RelOptPlanner planner = rootRel.getCluster().getPlanner();
+    final RelOptPlanner planner = root.rel.getCluster().getPlanner();
 
-    planner.setRoot(rootRel);
+    planner.setRoot(root.rel);
 
-    final RelTraitSet desiredTraits = getDesiredRootTraitSet(rootRel);
+    final RelTraitSet desiredTraits = getDesiredRootTraitSet(root);
     final Program program = getProgram();
 
     final DataContext dataContext = context.getDataContext();
@@ -144,14 +141,14 @@ public abstract class Prepare {
           new RelOptLattice(lattice.getLattice(), starRelOptTable));
     }
 
-    final RelNode rootRel4 = program.run(planner, rootRel, desiredTraits);
+    final RelNode rootRel4 = program.run(planner, root.rel, desiredTraits);
     if (LOGGER.isLoggable(Level.FINE)) {
       LOGGER.fine(
           "Plan after physical tweaks: "
           + RelOptUtil.toString(rootRel4, SqlExplainLevel.ALL_ATTRIBUTES));
     }
 
-    return rootRel4;
+    return root.withRel(rootRel4);
   }
 
   private Program getProgram() {
@@ -166,22 +163,21 @@ public abstract class Prepare {
     return Programs.standard();
   }
 
-  protected RelTraitSet getDesiredRootTraitSet(RelNode rootRel) {
+  protected RelTraitSet getDesiredRootTraitSet(RelRoot root) {
     // Make sure non-CallingConvention traits, if any, are preserved
-    return rootRel.getTraitSet()
-        .replace(resultConvention).simplify();
+    return root.rel.getTraitSet()
+        .replace(resultConvention)
+        .replace(root.collation)
+        .simplify();
   }
 
   /**
    * Implements a physical query plan.
    *
-   * @param rowType original row type returned by query validator
-   * @param rootRel root of the relational expression.
-   * @param sqlKind SqlKind of the original statement.
+   * @param root Root of the relational expression tree
    * @return an executable plan
    */
-  protected abstract PreparedResult implement(
-      RelDataType rowType, RelNode rootRel, SqlKind sqlKind);
+  protected abstract PreparedResult implement(RelRoot root);
 
   public PreparedResult prepareSql(
       SqlNode sqlQuery,
@@ -223,23 +219,14 @@ public abstract class Prepare {
       sqlToRelConverter.setIsExplain(sqlExplain.getDynamicParamCount());
     }
 
-    RelNode rootRel =
+    RelRoot root =
         sqlToRelConverter.convertQuery(sqlQuery, needsValidation, true);
-    Hook.CONVERTED.run(rootRel);
+    Hook.CONVERTED.run(root.rel);
 
     if (timingTracer != null) {
       timingTracer.traceTime("end sql2rel");
     }
 
-    // A query can have 0 collations and still be ordered (if it is ordered
-    // on a non-projected expression). But otherwise,
-    // ordered == !collations.isEmpty().
-    ordered = !SqlToRelConverter.isUnordered(sqlQuery);
-    assert collations.isEmpty();
-    if (rootRel instanceof Sort) {
-      collations.add(((Sort) rootRel).getCollation());
-    }
-
     final RelDataType resultType = validator.getValidatedNodeType(sqlQuery);
     fieldOrigins = validator.getFieldOrigins(sqlQuery);
     assert fieldOrigins.size() == resultType.getFieldCount();
@@ -258,24 +245,24 @@ public abstract class Prepare {
             resultType, parameterRowType, null, explainAsXml, detailLevel);
       case LOGICAL:
         return createPreparedExplanation(
-            null, parameterRowType, rootRel, explainAsXml, detailLevel);
+            null, parameterRowType, root, explainAsXml, detailLevel);
       default:
       }
     }
 
     // Structured type flattening, view expansion, and plugging in physical
     // storage.
-    rootRel = flattenTypes(rootRel, true);
+    root = root.withRel(flattenTypes(root.rel, true));
 
     if (this.context.config().forceDecorrelate()) {
       // Subquery decorrelation.
-      rootRel = decorrelate(sqlToRelConverter, sqlQuery, rootRel);
+      root = root.withRel(decorrelate(sqlToRelConverter, sqlQuery, root.rel));
     }
 
     // Trim unused fields.
-    rootRel = trimUnusedFields(rootRel);
+    root = trimUnusedFields(root);
 
-    Hook.TRIMMED.run(rootRel);
+    Hook.TRIMMED.run(root.rel);
 
     // Display physical plan after decorrelation.
     if (sqlExplain != null) {
@@ -285,13 +272,13 @@ public abstract class Prepare {
       switch (explainDepth) {
       case PHYSICAL:
       default:
-        rootRel = optimize(rootRel, materializations, lattices);
+        root = optimize(root, materializations, lattices);
         return createPreparedExplanation(
-            null, parameterRowType, rootRel, explainAsXml, detailLevel);
+            null, parameterRowType, root, explainAsXml, detailLevel);
       }
     }
 
-    rootRel = optimize(rootRel, materializations, lattices);
+    root = optimize(root, materializations, lattices);
 
     if (timingTracer != null) {
       timingTracer.traceTime("end optimization");
@@ -300,14 +287,10 @@ public abstract class Prepare {
     // For transformation from DML -> DML, use result of rewrite
     // (e.g. UPDATE -> MERGE).  For anything else (e.g. CALL -> SELECT),
     // use original kind.
-    SqlKind kind = sqlQuery.getKind();
-    if (!kind.belongsTo(SqlKind.DML)) {
-      kind = sqlNodeOriginal.getKind();
+    if (!root.kind.belongsTo(SqlKind.DML)) {
+      root = root.withKind(sqlNodeOriginal.getKind());
     }
-    return implement(
-        resultType,
-        rootRel,
-        kind);
+    return implement(root);
   }
 
   protected LogicalTableModify.Operation mapTableModOp(
@@ -350,15 +333,17 @@ public abstract class Prepare {
    * expression that projects
    * only the columns required by its consumer.
    *
-   * @param rootRel Relational expression that is at the root of the tree
+   * @param root Root of relational expression tree
    * @return Trimmed relational expression
    */
-  protected RelNode trimUnusedFields(RelNode rootRel) {
+  protected RelRoot trimUnusedFields(RelRoot root) {
     final SqlToRelConverter converter =
         getSqlToRelConverter(
             getSqlValidator(), catalogReader);
-    converter.setTrimUnusedFields(shouldTrim(rootRel));
-    return converter.trimUnusedFields(ordered, rootRel);
+    converter.setTrimUnusedFields(shouldTrim(root.rel));
+    final boolean ordered = !root.collation.getFieldCollations().isEmpty();
+    final boolean dml = SqlKind.DML.contains(root.kind);
+    return root.withRel(converter.trimUnusedFields(dml || ordered, root.rel));
   }
 
   private boolean shouldTrim(RelNode rootRel) {
@@ -377,9 +362,7 @@ public abstract class Prepare {
    * @param schemaPath List of schema names wherein to find referenced tables
    * @return Relational expression
    */
-  public RelNode expandView(
-      RelDataType rowType,
-      String queryString,
+  public RelRoot expandView(RelDataType rowType, String queryString,
       List<String> schemaPath) {
     throw new UnsupportedOperationException();
   }
@@ -413,28 +396,28 @@ public abstract class Prepare {
       implements PreparedResult {
     private final RelDataType rowType;
     private final RelDataType parameterRowType;
-    private final RelNode rel;
+    private final RelRoot root;
     private final boolean asXml;
     private final SqlExplainLevel detailLevel;
 
     public PreparedExplain(
         RelDataType rowType,
         RelDataType parameterRowType,
-        RelNode rel,
+        RelRoot root,
         boolean asXml,
         SqlExplainLevel detailLevel) {
       this.rowType = rowType;
       this.parameterRowType = parameterRowType;
-      this.rel = rel;
+      this.root = root;
       this.asXml = asXml;
       this.detailLevel = detailLevel;
     }
 
     public String getCode() {
-      if (rel == null) {
+      if (root == null) {
         return RelOptUtil.dumpType(rowType);
       } else {
-        return RelOptUtil.dumpPlan("", rel, asXml, detailLevel);
+        return RelOptUtil.dumpPlan("", root.rel, asXml, detailLevel);
       }
     }
 
@@ -455,10 +438,6 @@ public abstract class Prepare {
           Collections.<String>nCopies(4, null));
     }
 
-    public RelNode getRel() {
-      return rel;
-    }
-
     public abstract Bindable getBindable();
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java b/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java
index dee2596..aa3e8bf 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelCollationTraitDef.java
@@ -64,10 +64,6 @@ public class RelCollationTraitDef extends RelTraitDef<RelCollation> {
       RelNode rel,
       RelCollation toCollation,
       boolean allowInfiniteCostConverters) {
-    if (toCollation == RelCollations.PRESERVE) {
-      return null;
-    }
-
     if (toCollation.getFieldCollations().isEmpty()) {
       // An empty sort doesn't make sense.
       return null;
@@ -87,7 +83,7 @@ public class RelCollationTraitDef extends RelTraitDef<RelCollation> {
 
   public boolean canConvert(
       RelOptPlanner planner, RelCollation fromTrait, RelCollation toTrait) {
-    return toTrait != RelCollations.PRESERVE;
+    return true;
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/RelCollations.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelCollations.java b/core/src/main/java/org/apache/calcite/rel/RelCollations.java
index 36f1327..07a7410 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelCollations.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelCollations.java
@@ -43,6 +43,7 @@ public class RelCollations {
    * A collation that cannot be replicated by applying a sort. The only
    * implementation choice is to apply operations that preserve order.
    */
+  @Deprecated // to be removed before 2.0
   public static final RelCollation PRESERVE =
       RelCollationTraitDef.INSTANCE.canonize(
           new RelCollationImpl(

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/RelInput.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelInput.java b/core/src/main/java/org/apache/calcite/rel/RelInput.java
index a8678cb..6179c04 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelInput.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelInput.java
@@ -78,6 +78,12 @@ public interface RelInput {
 
   List<RexNode> getExpressionList(String tag);
 
+  List<String> getStringList(String tag);
+
+  List<Integer> getIntegerList(String tag);
+
+  List<List<Integer>> getIntegerListList(String tag);
+
   RelDataType getRowType(String tag);
 
   RelDataType getRowType(String expressionsTag, String fieldsTag);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/RelRoot.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/RelRoot.java b/core/src/main/java/org/apache/calcite/rel/RelRoot.java
new file mode 100644
index 0000000..77f3782
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/RelRoot.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel;
+
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.util.ImmutableIntList;
+import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.mapping.Mappings;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Root of a tree of {@link RelNode}.
+ *
+ * <p>One important reason that RelRoot exists is to deal with queries like
+ *
+ * <blockquote><code>SELECT name
+ * FROM emp
+ * ORDER BY empno DESC</code></blockquote>
+ *
+ * <p>Calcite knows that the result must be sorted, but cannot represent its
+ * sort order as a collation, because {@code empno} is not a field in the
+ * result.
+ *
+ * <p>Instead we represent this as
+ *
+ * <blockquote><code>RelRoot: {
+ *   rel: Sort($1 DESC)
+ *          Project(name, empno)
+ *            TableScan(EMP)
+ *   fields: [0]
+ *   collation: [1 DESC]
+ * }</code></blockquote>
+ *
+ * <p>Note that the {@code empno} field is present in the result, but the
+ * {@code fields} mask tells the consumer to throw it away.
+ *
+ * <p>Another use case is queries like this:
+ *
+ * <blockquote><code>SELECT name AS n, name AS n2, empno AS n
+ * FROM emp</code></blockquote>
+ *
+ * <p>The there are multiple uses of the {@code name} field. and there are
+ * multiple columns aliased as {@code n}. You can represent this as
+ *
+ * <blockquote><code>RelRoot: {
+ *   rel: Project(name, empno)
+ *          TableScan(EMP)
+ *   fields: [(0, "n"), (0, "n2"), (1, "n")]
+ *   collation: []
+ * }</code></blockquote>
+ */
+public class RelRoot {
+  public final RelNode rel;
+  public final RelDataType validatedRowType;
+  public final SqlKind kind;
+  public final ImmutableList<Pair<Integer, String>> fields;
+  public final RelCollation collation;
+
+  /**
+   * Creates a RelRoot.
+   *
+   * @param validatedRowType Original row type returned by query validator
+   * @param kind Type of query (SELECT, UPDATE, ...)
+   */
+
+  public RelRoot(RelNode rel, RelDataType validatedRowType, SqlKind kind,
+       List<Pair<Integer, String>> fields, RelCollation collation) {
+    this.rel = rel;
+    this.validatedRowType = validatedRowType;
+    this.kind = kind;
+    this.fields = ImmutableList.copyOf(fields);
+    this.collation = Preconditions.checkNotNull(collation);
+  }
+
+  /** Creates a simple RelRoot. */
+  public static RelRoot of(RelNode rel, SqlKind kind) {
+    return of(rel, rel.getRowType(), kind);
+  }
+
+  /** Creates a simple RelRoot. */
+  public static RelRoot of(RelNode rel, RelDataType rowType, SqlKind kind) {
+    final ImmutableIntList refs =
+        ImmutableIntList.identity(rowType.getFieldCount());
+    final List<String> names = rowType.getFieldNames();
+    return new RelRoot(rel, rowType, kind, Pair.zip(refs, names),
+        RelCollations.EMPTY);
+  }
+
+  /** Creates a copy of this RelRoot, assigning a {@link RelNode}. */
+  public RelRoot withRel(RelNode rel) {
+    if (rel == this.rel) {
+      return this;
+    }
+    return new RelRoot(rel, validatedRowType, kind, fields, collation);
+  }
+
+  /** Creates a copy, assigning a new kind. */
+  public RelRoot withKind(SqlKind kind) {
+    if (kind == this.kind) {
+      return this;
+    }
+    return new RelRoot(rel, validatedRowType, kind, fields, collation);
+  }
+
+  public RelRoot withCollation(RelCollation collation) {
+    return new RelRoot(rel, validatedRowType, kind, fields, collation);
+  }
+
+  /** Returns the root relational expression, creating a {@link LogicalProject}
+   * if necessary to remove fields that are not needed. */
+  public RelNode project() {
+    if (isRefTrivial()) {
+      return rel;
+    }
+    final List<RexNode> projects = new ArrayList<>();
+    final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+    for (Pair<Integer, String> field : fields) {
+      projects.add(rexBuilder.makeInputRef(rel, field.left));
+    }
+    return LogicalProject.create(rel, projects, Pair.right(fields));
+  }
+
+  public boolean isNameTrivial() {
+    final RelDataType inputRowType = rel.getRowType();
+    return Pair.right(fields).equals(inputRowType.getFieldNames());
+  }
+
+  public boolean isRefTrivial() {
+    if (SqlKind.DML.contains(kind)) {
+      // DML statements return a single count column.
+      // The validated type is of the SELECT.
+      // Still, we regard the mapping as trivial.
+      return true;
+    }
+    final RelDataType inputRowType = rel.getRowType();
+    return Mappings.isIdentity(Pair.left(fields), inputRowType.getFieldCount());
+  }
+
+  public boolean isCollationTrivial() {
+    final List<RelCollation> collations = rel.getTraitSet()
+        .getTraits(RelCollationTraitDef.INSTANCE);
+    return collations != null
+        && collations.size() == 1
+        && collations.get(0).equals(collation);
+  }
+}
+
+// End RelRoot.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/SingleRel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/SingleRel.java b/core/src/main/java/org/apache/calcite/rel/SingleRel.java
index 27e0bf6..5fbb2c6 100644
--- a/core/src/main/java/org/apache/calcite/rel/SingleRel.java
+++ b/core/src/main/java/org/apache/calcite/rel/SingleRel.java
@@ -35,7 +35,7 @@ import java.util.List;
 public abstract class SingleRel extends AbstractRelNode {
   //~ Instance fields --------------------------------------------------------
 
-  private RelNode input;
+  protected RelNode input;
 
   //~ Constructors -----------------------------------------------------------
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
index b3f9a56..57a7f76 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
@@ -60,24 +60,24 @@ public class RelJsonReader {
 
   private final RelOptCluster cluster;
   private final RelOptSchema relOptSchema;
-  private final Schema schema;
   private final RelJson relJson = new RelJson(null);
-  private final Map<String, RelNode> relMap =
-      new LinkedHashMap<String, RelNode>();
+  private final Map<String, RelNode> relMap = new LinkedHashMap<>();
   private RelNode lastRel;
 
   public RelJsonReader(RelOptCluster cluster, RelOptSchema relOptSchema,
       Schema schema) {
     this.cluster = cluster;
     this.relOptSchema = relOptSchema;
-    this.schema = schema;
+    Util.discard(schema);
   }
 
   public RelNode read(String s) throws IOException {
     lastRel = null;
     final ObjectMapper mapper = new ObjectMapper();
     Map<String, Object> o = mapper.readValue(s, TYPE_REF);
-    readRels((List<Map<String, Object>>) o.get("rels"));
+    @SuppressWarnings("unchecked")
+    final List<Map<String, Object>> rels = (List) o.get("rels");
+    readRels(rels);
     System.out.println(lastRel);
     return lastRel;
   }
@@ -102,7 +102,7 @@ public class RelJsonReader {
       }
 
       public RelOptTable getTable(String table) {
-        final List<String> list = (List<String>) jsonRel.get(table);
+        final List<String> list = getStringList(table);
         return relOptSchema.getTableForMember(list);
       }
 
@@ -113,11 +113,11 @@ public class RelJsonReader {
       }
 
       public List<RelNode> getInputs() {
-        List<String> jsonInputs = (List<String>) jsonRel.get("inputs");
+        final List<String> jsonInputs = getStringList("inputs");
         if (jsonInputs == null) {
           return ImmutableList.of(lastRel);
         }
-        final List<RelNode> inputs = new ArrayList<RelNode>();
+        final List<RelNode> inputs = new ArrayList<>();
         for (String jsonInput : jsonInputs) {
           inputs.add(lookupInput(jsonInput));
         }
@@ -145,17 +145,25 @@ public class RelJsonReader {
         return builder.build();
       }
 
+      public List<String> getStringList(String tag) {
+        //noinspection unchecked
+        return (List<String>) jsonRel.get(tag);
+      }
+
       public List<Integer> getIntegerList(String tag) {
+        //noinspection unchecked
         return (List<Integer>) jsonRel.get(tag);
       }
 
       public List<List<Integer>> getIntegerListList(String tag) {
+        //noinspection unchecked
         return (List<List<Integer>>) jsonRel.get(tag);
       }
 
       public List<AggregateCall> getAggregateCalls(String tag) {
-        List<Map<String, Object>> jsonAggs = (List) jsonRel.get(tag);
-        final List<AggregateCall> inputs = new ArrayList<AggregateCall>();
+        @SuppressWarnings("unchecked")
+        final List<Map<String, Object>> jsonAggs = (List) jsonRel.get(tag);
+        final List<AggregateCall> inputs = new ArrayList<>();
         for (Map<String, Object> jsonAggCall : jsonAggs) {
           inputs.add(toAggCall(jsonAggCall));
         }
@@ -184,8 +192,9 @@ public class RelJsonReader {
       }
 
       public List<RexNode> getExpressionList(String tag) {
+        @SuppressWarnings("unchecked")
         final List<Object> jsonNodes = (List) jsonRel.get(tag);
-        final List<RexNode> nodes = new ArrayList<RexNode>();
+        final List<RexNode> nodes = new ArrayList<>();
         for (Object jsonNode : jsonNodes) {
           nodes.add(relJson.toRex(this, jsonNode));
         }
@@ -215,6 +224,7 @@ public class RelJsonReader {
       }
 
       public RelCollation getCollation() {
+        //noinspection unchecked
         return relJson.toCollation((List) get("collation"));
       }
 
@@ -223,7 +233,8 @@ public class RelJsonReader {
       }
 
       public ImmutableList<ImmutableList<RexLiteral>> getTuples(String tag) {
-        List<List> jsonTuples = (List) get(tag);
+        //noinspection unchecked
+        final List<List> jsonTuples = (List) get(tag);
         final ImmutableList.Builder<ImmutableList<RexLiteral>> builder =
             ImmutableList.builder();
         for (List jsonTuple : jsonTuples) {
@@ -245,9 +256,7 @@ public class RelJsonReader {
       final RelNode rel = (RelNode) constructor.newInstance(input);
       relMap.put(id, rel);
       lastRel = rel;
-    } catch (InstantiationException e) {
-      throw new RuntimeException(e);
-    } catch (IllegalAccessException e) {
+    } catch (InstantiationException | IllegalAccessException e) {
       throw new RuntimeException(e);
     } catch (InvocationTargetException e) {
       final Throwable e2 = e.getCause();
@@ -263,6 +272,7 @@ public class RelJsonReader {
     final SqlAggFunction aggregation =
         relJson.toAggregation(aggName, jsonAggCall);
     final Boolean distinct = (Boolean) jsonAggCall.get("distinct");
+    @SuppressWarnings("unchecked")
     final List<Integer> operands = (List<Integer>) jsonAggCall.get("operands");
     final Integer filterOperand = (Integer) jsonAggCall.get("filter");
     final RelDataType type =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
index de7f67f..e9c7c9c 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
@@ -200,7 +200,7 @@ public class AggregateStarTableRule extends RelOptRule {
       if (roll == null) {
         break tryRoll;
       }
-      return AggregateCall.create(roll, false, ImmutableList.of(offset + i),
+      return AggregateCall.create(roll, false, ImmutableList.of(offset + i), -1,
           groupCount, input, null, aggregateCall.name);
     }
 
@@ -215,7 +215,7 @@ public class AggregateStarTableRule extends RelOptRule {
         }
         newArgs.add(z);
       }
-      return AggregateCall.create(aggregation, false, newArgs,
+      return AggregateCall.create(aggregation, false, newArgs, -1,
           groupCount, input, null, aggregateCall.name);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
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 1cac0f8..f7e6a9e 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -754,10 +754,6 @@ public class RexUtil {
       List<RelCollation> collationList) {
     final List<RelCollation> newCollationList = new ArrayList<>();
     for (RelCollation collation : collationList) {
-      if (collation == RelCollations.PRESERVE) {
-        newCollationList.add(collation);
-        continue;
-      }
       final List<RelFieldCollation> newFieldCollationList = new ArrayList<>();
       for (RelFieldCollation fieldCollation
           : collation.getFieldCollations()) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java b/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java
index b3a8ff7..1da29ef 100644
--- a/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java
+++ b/core/src/main/java/org/apache/calcite/schema/impl/ViewTable.java
@@ -25,6 +25,7 @@ import org.apache.calcite.linq4j.Queryable;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeImpl;
@@ -116,19 +117,16 @@ public class ViewTable
   public RelNode toRel(
       RelOptTable.ToRelContext context,
       RelOptTable relOptTable) {
-    return expandView(context, relOptTable.getRowType(), viewSql);
+    return expandView(context, relOptTable.getRowType(), viewSql).rel;
   }
 
-  private RelNode expandView(
-      RelOptTable.ToRelContext preparingStmt,
-      RelDataType rowType,
-      String queryString) {
+  private RelRoot expandView(RelOptTable.ToRelContext preparingStmt,
+      RelDataType rowType, String queryString) {
     try {
-      RelNode rel = preparingStmt.expandView(rowType, queryString, schemaPath);
+      RelRoot root = preparingStmt.expandView(rowType, queryString, schemaPath);
 
-      rel = RelOptUtil.createCastRel(rel, rowType, true);
-      //rel = viewExpander.flattenTypes(rel, false);
-      return rel;
+      root = root.withRel(RelOptUtil.createCastRel(root.rel, rowType, true));
+      return root;
     } catch (Throwable e) {
       throw Util.newInternal(
           e, "Error while parsing view definition:  " + queryString);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
----------------------------------------------------------------------
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 f4765b4..4d8f0a5 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
@@ -343,9 +343,13 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
 
   public void rewriteRel(LogicalTableModify rel) {
     LogicalTableModify newRel =
-        LogicalTableModify.create(rel.getTable(), rel.getCatalogReader(),
-            getNewForOldRel(rel.getInput()), rel.getOperation(),
-            rel.getUpdateColumnList(), true);
+        LogicalTableModify.create(
+            rel.getTable(),
+            rel.getCatalogReader(),
+            getNewForOldRel(rel.getInput()),
+            rel.getOperation(),
+            rel.getUpdateColumnList(),
+            true);
     setNewForOldRel(rel, newRel);
   }
 
@@ -394,8 +398,7 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
 
   public void rewriteRel(LogicalJoin rel) {
     LogicalJoin newRel =
-        LogicalJoin.create(
-            getNewForOldRel(rel.getLeft()),
+        LogicalJoin.create(getNewForOldRel(rel.getLeft()),
             getNewForOldRel(rel.getRight()),
             rel.getCondition().accept(new RewriteRexShuttle()),
             rel.getJoinType(),

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/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 ae73586..50f22c1 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -31,6 +31,7 @@ import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.RelShuttle;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
@@ -42,6 +43,7 @@ import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.core.Sample;
+import org.apache.calcite.rel.core.Sort;
 import org.apache.calcite.rel.core.Uncollect;
 import org.apache.calcite.rel.logical.LogicalAggregate;
 import org.apache.calcite.rel.logical.LogicalCorrelate;
@@ -56,6 +58,7 @@ import org.apache.calcite.rel.logical.LogicalTableScan;
 import org.apache.calcite.rel.logical.LogicalUnion;
 import org.apache.calcite.rel.logical.LogicalValues;
 import org.apache.calcite.rel.metadata.RelColumnMapping;
+import org.apache.calcite.rel.stream.Delta;
 import org.apache.calcite.rel.stream.LogicalDelta;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -102,6 +105,7 @@ import org.apache.calcite.sql.SqlNodeList;
 import org.apache.calcite.sql.SqlNumericLiteral;
 import org.apache.calcite.sql.SqlOperator;
 import org.apache.calcite.sql.SqlOperatorTable;
+import org.apache.calcite.sql.SqlOrderBy;
 import org.apache.calcite.sql.SqlSampleSpec;
 import org.apache.calcite.sql.SqlSelect;
 import org.apache.calcite.sql.SqlSelectKeyword;
@@ -425,26 +429,36 @@ public class SqlToRelConverter {
   }
 
   private void checkConvertedType(SqlNode query, RelNode result) {
-    if (!query.isA(SqlKind.DML)) {
-      // Verify that conversion from SQL to relational algebra did
-      // not perturb any type information.  (We can't do this if the
-      // SQL statement is something like an INSERT which has no
-      // validator type information associated with its result,
-      // hence the namespace check above.)
-      RelDataType convertedRowType = result.getRowType();
-      if (!checkConvertedRowType(query, convertedRowType)) {
-        RelDataType validatedRowType =
-            validator.getValidatedNodeType(query);
-        validatedRowType = uniquifyFields(validatedRowType);
-        throw Util.newInternal("Conversion to relational algebra failed to "
-            + "preserve datatypes:\n"
-            + "validated type:\n"
-            + validatedRowType.getFullTypeString()
-            + "\nconverted type:\n"
-            + convertedRowType.getFullTypeString()
-            + "\nrel:\n"
-            + RelOptUtil.toString(result));
-      }
+    if (query.isA(SqlKind.DML)) {
+      return;
+    }
+    // Verify that conversion from SQL to relational algebra did
+    // not perturb any type information.  (We can't do this if the
+    // SQL statement is something like an INSERT which has no
+    // validator type information associated with its result,
+    // hence the namespace check above.)
+    final List<RelDataTypeField> validatedFields =
+        validator.getValidatedNodeType(query).getFieldList();
+    final RelDataType validatedRowType =
+        validator.getTypeFactory().createStructType(
+            Pair.right(validatedFields),
+            SqlValidatorUtil.uniquify(Pair.left(validatedFields)));
+
+    final List<RelDataTypeField> convertedFields =
+        result.getRowType().getFieldList().subList(0, validatedFields.size());
+    final RelDataType convertedRowType =
+        validator.getTypeFactory().createStructType(convertedFields);
+
+    if (!RelOptUtil.equal("validated row type", validatedRowType,
+        "converted row type", convertedRowType, false)) {
+      throw Util.newInternal("Conversion to relational algebra failed to "
+          + "preserve datatypes:\n"
+          + "validated type:\n"
+          + validatedRowType.getFullTypeString()
+          + "\nconverted type:\n"
+          + convertedRowType.getFullTypeString()
+          + "\nrel:\n"
+          + RelOptUtil.toString(result));
     }
   }
 
@@ -501,7 +515,10 @@ public class SqlToRelConverter {
       final List<RelCollation> collations =
           rootRel.getTraitSet().getTraits(RelCollationTraitDef.INSTANCE);
       rootRel = trimmer.trim(rootRel);
-      if (!ordered && collations != null) {
+      if (!ordered
+          && collations != null
+          && !collations.isEmpty()
+          && !collations.equals(ImmutableList.of(RelCollations.EMPTY))) {
         final RelTraitSet traitSet = rootRel.getTraitSet()
             .replace(RelCollationTraitDef.INSTANCE, collations);
         rootRel = rootRel.copy(traitSet, rootRel.getInputs());
@@ -539,7 +556,7 @@ public class SqlToRelConverter {
    *                        will become a JDBC result set; <code>false</code> if
    *                        the query will be part of a view.
    */
-  public RelNode convertQuery(
+  public RelRoot convertQuery(
       SqlNode query,
       final boolean needsValidation,
       final boolean top) {
@@ -547,13 +564,17 @@ public class SqlToRelConverter {
       query = validator.validate(query);
     }
 
-    RelNode result = convertQueryRecursive(query, top, null);
-    if (top && isStream(query)) {
-      result = new LogicalDelta(cluster, result.getTraitSet(), result);
+    RelNode result = convertQueryRecursive(query, top, null).rel;
+    if (top) {
+      if (isStream(query)) {
+        result = new LogicalDelta(cluster, result.getTraitSet(), result);
+      }
     }
-    if (isUnordered(query)) {
-      result = result.copy(result.getTraitSet().replace(RelCollations.EMPTY),
-          result.getInputs());
+    RelCollation collation = RelCollations.EMPTY;
+    if (!query.isA(SqlKind.DML)) {
+      if (isOrdered(query)) {
+        collation = requiredCollation(result);
+      }
     }
     checkConvertedType(query, result);
 
@@ -567,7 +588,9 @@ public class SqlToRelConverter {
               SqlExplainLevel.EXPPLAN_ATTRIBUTES));
     }
 
-    return result;
+    final RelDataType validatedRowType = validator.getValidatedNodeType(query);
+    return RelRoot.of(result, validatedRowType, query.getKind())
+        .withCollation(collation);
   }
 
   private static boolean isStream(SqlNode query) {
@@ -575,33 +598,39 @@ public class SqlToRelConverter {
         && ((SqlSelect) query).isKeywordPresent(SqlSelectKeyword.STREAM);
   }
 
-  public static boolean isUnordered(SqlNode query) {
-    return query instanceof SqlSelect
-        && ((SqlSelect) query).getOrderList() == null;
-  }
-
-  protected boolean checkConvertedRowType(
-      SqlNode query,
-      RelDataType convertedRowType) {
-    RelDataType validatedRowType = validator.getValidatedNodeType(query);
-    validatedRowType = uniquifyFields(validatedRowType);
-
-    return RelOptUtil.equal("validated row type", validatedRowType,
-        "converted row type", convertedRowType, false);
+  public static boolean isOrdered(SqlNode query) {
+    switch (query.getKind()) {
+    case SELECT:
+      return ((SqlSelect) query).getOrderList() != null
+          && ((SqlSelect) query).getOrderList().size() > 0;
+    case WITH:
+      return isOrdered(((SqlWith) query).body);
+    case ORDER_BY:
+      return ((SqlOrderBy) query).orderList.size() > 0;
+    default:
+      return false;
+    }
   }
 
-  protected RelDataType uniquifyFields(RelDataType rowType) {
-    return validator.getTypeFactory().createStructType(
-        RelOptUtil.getFieldTypeList(rowType),
-        SqlValidatorUtil.uniquify(rowType.getFieldNames()));
+  private RelCollation requiredCollation(RelNode r) {
+    if (r instanceof Sort) {
+      return ((Sort) r).collation;
+    }
+    if (r instanceof Project) {
+      return requiredCollation(((Project) r).getInput());
+    }
+    if (r instanceof Delta) {
+      return requiredCollation(((Delta) r).getInput());
+    }
+    throw new AssertionError();
   }
 
   /**
    * Converts a SELECT statement's parse tree into a relational expression.
    */
-  public RelNode convertSelect(SqlSelect select) {
+  public RelNode convertSelect(SqlSelect select, boolean top) {
     final SqlValidatorScope selectScope = validator.getWhereScope(select);
-    final Blackboard bb = createBlackboard(selectScope, null);
+    final Blackboard bb = createBlackboard(selectScope, null, top);
     convertSelectImpl(bb, select);
     return bb.root;
   }
@@ -609,15 +638,14 @@ public class SqlToRelConverter {
   /**
    * Factory method for creating translation workspace.
    */
-  protected Blackboard createBlackboard(
-      SqlValidatorScope scope,
-      Map<String, RexNode> nameToNodeMap) {
-    return new Blackboard(scope, nameToNodeMap);
+  protected Blackboard createBlackboard(SqlValidatorScope scope,
+      Map<String, RexNode> nameToNodeMap, boolean top) {
+    return new Blackboard(scope, nameToNodeMap, top);
   }
 
   /**
-   * Implementation of {@link #convertSelect(SqlSelect)}; derived class may
-   * override.
+   * Implementation of {@link #convertSelect(SqlSelect, boolean)};
+   * derived class may override.
    */
   protected void convertSelectImpl(
       final Blackboard bb,
@@ -796,8 +824,11 @@ public class SqlToRelConverter {
         false);
 
     // If extra expressions were added to the project list for sorting,
-    // add another project to remove them.
-    if (orderExprList.size() > 0) {
+    // add another project to remove them. But make the collation empty, because
+    // we can't represent the real collation.
+    //
+    // If it is the top node, use the real collation, but don't trim fields.
+    if (orderExprList.size() > 0 && !bb.top) {
       final List<RexNode> exprs = new ArrayList<>();
       final RelDataType rowType = bb.root.getRowType();
       final int fieldCount =
@@ -806,13 +837,8 @@ public class SqlToRelConverter {
         exprs.add(rexBuilder.makeInputRef(bb.root, i));
       }
       bb.setRoot(
-          new LogicalProject(
-              cluster,
-              cluster.traitSetOf(RelCollations.PRESERVE),
-              bb.root,
-              exprs,
-              cluster.getTypeFactory().createStructType(
-                  rowType.getFieldList().subList(0, fieldCount))),
+          LogicalProject.create(bb.root, exprs,
+              rowType.getFieldNames().subList(0, fieldCount)),
           false);
     }
   }
@@ -1437,7 +1463,7 @@ public class SqlToRelConverter {
         (seek instanceof SqlSelect)
             ? validator.getSelectScope((SqlSelect) seek)
             : null;
-    final Blackboard seekBb = createBlackboard(seekScope, null);
+    final Blackboard seekBb = createBlackboard(seekScope, null, false);
     RelNode seekRel = convertQueryOrInList(seekBb, seek, targetDataType);
 
     return RelOptUtil.createExistsPlan(seekRel, subqueryType, logic,
@@ -1462,7 +1488,7 @@ public class SqlToRelConverter {
           false,
           targetRowType);
     } else {
-      return convertQueryRecursive(seek, false, null);
+      return convertQueryRecursive(seek, false, null).project();
     }
   }
 
@@ -1721,10 +1747,9 @@ public class SqlToRelConverter {
   public RexNode convertExpression(
       SqlNode node) {
     Map<String, RelDataType> nameToTypeMap = Collections.emptyMap();
-    Blackboard bb =
-        createBlackboard(
-            new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap),
-            null);
+    final ParameterScope scope =
+        new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap);
+    final Blackboard bb = createBlackboard(scope, null, false);
     return bb.convertExpression(node);
   }
 
@@ -1746,10 +1771,9 @@ public class SqlToRelConverter {
     for (Map.Entry<String, RexNode> entry : nameToNodeMap.entrySet()) {
       nameToTypeMap.put(entry.getKey(), entry.getValue().getType());
     }
-    Blackboard bb =
-        createBlackboard(
-            new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap),
-            nameToNodeMap);
+    final ParameterScope scope =
+        new ParameterScope((SqlValidatorImpl) validator, nameToTypeMap);
+    final Blackboard bb = createBlackboard(scope, nameToNodeMap, false);
     return bb.convertExpression(node);
   }
 
@@ -1925,20 +1949,22 @@ public class SqlToRelConverter {
 
     case JOIN:
       final SqlJoin join = (SqlJoin) from;
-      final Blackboard fromBlackboard =
-          createBlackboard(validator.getJoinScope(from), null);
+      final SqlValidatorScope scope = validator.getJoinScope(from);
+      final Blackboard fromBlackboard = createBlackboard(scope, null, false);
       SqlNode left = join.getLeft();
       SqlNode right = join.getRight();
       final boolean isNatural = join.isNatural();
       final JoinType joinType = join.getJoinType();
+      final SqlValidatorScope leftScope =
+          Util.first(validator.getJoinScope(left),
+              ((DelegatingScope) bb.scope).getParent());
       final Blackboard leftBlackboard =
-          createBlackboard(
-              Util.first(validator.getJoinScope(left),
-                  ((DelegatingScope) bb.scope).getParent()), null);
+          createBlackboard(leftScope, null, false);
+      final SqlValidatorScope rightScope =
+          Util.first(validator.getJoinScope(right),
+              ((DelegatingScope) bb.scope).getParent());
       final Blackboard rightBlackboard =
-          createBlackboard(
-              Util.first(validator.getJoinScope(right),
-                  ((DelegatingScope) bb.scope).getParent()), null);
+          createBlackboard(rightScope, null, false);
       convertFrom(leftBlackboard, left);
       RelNode leftRel = leftBlackboard.root;
       convertFrom(rightBlackboard, right);
@@ -1981,7 +2007,7 @@ public class SqlToRelConverter {
     case INTERSECT:
     case EXCEPT:
     case UNION:
-      final RelNode rel = convertQueryRecursive(from, false, null);
+      final RelNode rel = convertQueryRecursive(from, false, null).project();
       bb.setRoot(rel, true);
       return;
 
@@ -2029,7 +2055,7 @@ public class SqlToRelConverter {
       datasetStack.push(sampleName);
       SqlCall cursorCall = call.operand(1);
       SqlNode query = cursorCall.operand(0);
-      RelNode converted = convertQuery(query, false, false);
+      RelNode converted = convertQuery(query, false, false).rel;
       bb.setRoot(converted, false);
       datasetStack.pop();
       return;
@@ -2788,29 +2814,28 @@ public class SqlToRelConverter {
    * @param targetRowType Target row type, or null
    * @return Relational expression
    */
-  protected RelNode convertQueryRecursive(
-      SqlNode query,
-      boolean top,
+  protected RelRoot convertQueryRecursive(SqlNode query, boolean top,
       RelDataType targetRowType) {
-    switch (query.getKind()) {
+    final SqlKind kind = query.getKind();
+    switch (kind) {
     case SELECT:
-      return convertSelect((SqlSelect) query);
+      return RelRoot.of(convertSelect((SqlSelect) query, top), kind);
     case INSERT:
-      return convertInsert((SqlInsert) query);
+      return RelRoot.of(convertInsert((SqlInsert) query), kind);
     case DELETE:
-      return convertDelete((SqlDelete) query);
+      return RelRoot.of(convertDelete((SqlDelete) query), kind);
     case UPDATE:
-      return convertUpdate((SqlUpdate) query);
+      return RelRoot.of(convertUpdate((SqlUpdate) query), kind);
     case MERGE:
-      return convertMerge((SqlMerge) query);
+      return RelRoot.of(convertMerge((SqlMerge) query), kind);
     case UNION:
     case INTERSECT:
     case EXCEPT:
-      return convertSetOp((SqlCall) query);
+      return RelRoot.of(convertSetOp((SqlCall) query), kind);
     case WITH:
-      return convertWith((SqlWith) query);
+      return convertWith((SqlWith) query, top);
     case VALUES:
-      return convertValues((SqlCall) query, targetRowType);
+      return RelRoot.of(convertValues((SqlCall) query, targetRowType), kind);
     default:
       throw Util.newInternal("not a query: " + query);
     }
@@ -2824,8 +2849,10 @@ public class SqlToRelConverter {
    * @return Relational expression
    */
   protected RelNode convertSetOp(SqlCall call) {
-    final RelNode left = convertQueryRecursive(call.operand(0), false, null);
-    final RelNode right = convertQueryRecursive(call.operand(1), false, null);
+    final RelNode left =
+        convertQueryRecursive(call.operand(0), false, null).project();
+    final RelNode right =
+        convertQueryRecursive(call.operand(1), false, null).project();
     boolean all = false;
     if (call.getOperator() instanceof SqlSetOperator) {
       all = ((SqlSetOperator) (call.getOperator())).isAll();
@@ -2864,8 +2891,7 @@ public class SqlToRelConverter {
         validator.getValidatedNodeType(call);
     assert targetRowType != null;
     RelNode sourceRel =
-        convertQueryRecursive(
-            call.getSource(), false, targetRowType);
+        convertQueryRecursive(call.getSource(), false, targetRowType).project();
     RelNode massagedRel = convertColumnList(call, sourceRel);
 
     return createModify(targetTable, massagedRel);
@@ -2953,9 +2979,7 @@ public class SqlToRelConverter {
         return cluster;
       }
 
-      public RelNode expandView(
-          RelDataType rowType,
-          String queryString,
+      public RelRoot expandView(RelDataType rowType, String queryString,
           List<String> schemaPath) {
         return viewExpander.expandView(rowType, queryString, schemaPath);
       }
@@ -3084,7 +3108,7 @@ public class SqlToRelConverter {
 
   private RelNode convertDelete(SqlDelete call) {
     RelOptTable targetTable = getTargetTable(call);
-    RelNode sourceRel = convertSelect(call.getSourceSelect());
+    RelNode sourceRel = convertSelect(call.getSourceSelect(), false);
     return LogicalTableModify.create(targetTable, catalogReader, sourceRel,
         LogicalTableModify.Operation.DELETE, null, false);
   }
@@ -3100,7 +3124,7 @@ public class SqlToRelConverter {
       targetColumnNameList.add(name);
     }
 
-    RelNode sourceRel = convertSelect(call.getSourceSelect());
+    RelNode sourceRel = convertSelect(call.getSourceSelect(), false);
 
     return LogicalTableModify.create(targetTable, catalogReader, sourceRel,
         LogicalTableModify.Operation.UPDATE, targetColumnNameList, false);
@@ -3129,7 +3153,7 @@ public class SqlToRelConverter {
 
     // first, convert the merge's source select to construct the columns
     // from the target table and the set expressions in the update call
-    RelNode mergeSourceRel = convertSelect(call.getSourceSelect());
+    RelNode mergeSourceRel = convertSelect(call.getSourceSelect(), false);
 
     // then, convert the insert statement so we can get the insert
     // values expressions
@@ -3199,7 +3223,6 @@ public class SqlToRelConverter {
     final SqlQualified qualified;
     if (bb.scope != null) {
       qualified = bb.scope.fullyQualify(identifier);
-      identifier = qualified.identifier;
     } else {
       qualified = SqlQualified.create(null, 1, null, identifier);
     }
@@ -3271,7 +3294,7 @@ public class SqlToRelConverter {
     final SqlCall cursorCall = (SqlCall) subQuery.node;
     assert cursorCall.operandCount() == 1;
     SqlNode query = cursorCall.operand(0);
-    RelNode converted = convertQuery(query, false, false);
+    RelNode converted = convertQuery(query, false, false).rel;
     int iCursor = bb.cursors.size();
     bb.cursors.add(converted);
     subQuery.expr =
@@ -3305,21 +3328,18 @@ public class SqlToRelConverter {
       if (op == SqlStdOperatorTable.MULTISET_VALUE) {
         final SqlNodeList list =
             new SqlNodeList(call.getOperandList(), call.getParserPosition());
-//                assert bb.scope instanceof SelectScope : bb.scope;
         CollectNamespace nss =
             (CollectNamespace) validator.getNamespace(call);
         Blackboard usedBb;
         if (null != nss) {
-          usedBb = createBlackboard(nss.getScope(), null);
+          usedBb = createBlackboard(nss.getScope(), null, false);
         } else {
           usedBb =
-              createBlackboard(
-                  new ListScope(bb.scope) {
-                    public SqlNode getNode() {
-                      return call;
-                    }
-                  },
-                  null);
+              createBlackboard(new ListScope(bb.scope) {
+                public SqlNode getNode() {
+                  return call;
+                }
+              }, null, false);
         }
         RelDataType multisetType = validator.getValidatedNodeType(call);
         validator.setValidatedNodeType(
@@ -3327,7 +3347,8 @@ public class SqlToRelConverter {
             multisetType.getComponentType());
         input = convertQueryOrInList(usedBb, list, null);
       } else {
-        input = convertQuery(call.operand(0), false, true);
+        final RelRoot root = convertQuery(call.operand(0), false, true);
+        input = root.rel;
       }
 
       if (lastList.size() > 0) {
@@ -3492,8 +3513,8 @@ public class SqlToRelConverter {
   /**
    * Converts a WITH sub-query into a relational expression.
    */
-  public RelNode convertWith(SqlWith with) {
-    return convertQuery(with.body, false, false);
+  public RelRoot convertWith(SqlWith with, boolean top) {
+    return convertQuery(with.body, false, top);
   }
 
   /**
@@ -3504,7 +3525,7 @@ public class SqlToRelConverter {
       RelDataType targetRowType) {
     final SqlValidatorScope scope = validator.getOverScope(values);
     assert scope != null;
-    final Blackboard bb = createBlackboard(scope, null);
+    final Blackboard bb = createBlackboard(scope, null, false);
     convertValuesImpl(bb, values, targetRowType);
     return bb.root;
   }
@@ -3538,7 +3559,7 @@ public class SqlToRelConverter {
     final List<RelNode> unionRels = new ArrayList<>();
     for (SqlNode rowConstructor1 : values.getOperandList()) {
       SqlCall rowConstructor = (SqlCall) rowConstructor1;
-      Blackboard tmpBb = createBlackboard(bb.scope, null);
+      Blackboard tmpBb = createBlackboard(bb.scope, null, false);
       replaceSubqueries(tmpBb, rowConstructor,
           RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
       final List<Pair<RexNode, String>> exps = new ArrayList<>();
@@ -3604,7 +3625,7 @@ public class SqlToRelConverter {
     private final Map<String, RexNode> mapCorrelateVariableToRexNode =
         new HashMap<>();
 
-    List<RelNode> cursors;
+    final List<RelNode> cursors = new ArrayList<>();
 
     /**
      * List of <code>IN</code> and <code>EXISTS</code> nodes inside this
@@ -3637,6 +3658,7 @@ public class SqlToRelConverter {
         new ArrayList<>();
 
     private final List<RelDataTypeField> systemFieldList = new ArrayList<>();
+    final boolean top;
 
     /**
      * Creates a Blackboard.
@@ -3647,13 +3669,13 @@ public class SqlToRelConverter {
      * @param nameToNodeMap Map which translates the expression to map a
      *                      given parameter into, if translating expressions;
      *                      null otherwise
+     * @param top           Whether this is the root of the query
      */
-    protected Blackboard(
-        SqlValidatorScope scope,
-        Map<String, RexNode> nameToNodeMap) {
+    protected Blackboard(SqlValidatorScope scope,
+        Map<String, RexNode> nameToNodeMap, boolean top) {
       this.scope = scope;
       this.nameToNodeMap = nameToNodeMap;
-      this.cursors = new ArrayList<>();
+      this.top = top;
       subqueryNeedsOuterJoin = false;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a397063/core/src/main/java/org/apache/calcite/tools/Planner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/Planner.java b/core/src/main/java/org/apache/calcite/tools/Planner.java
index 98c1649..73c8047 100644
--- a/core/src/main/java/org/apache/calcite/tools/Planner.java
+++ b/core/src/main/java/org/apache/calcite/tools/Planner.java
@@ -18,6 +18,7 @@ package org.apache.calcite.tools;
 
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelRoot;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.sql.SqlNode;
 import org.apache.calcite.sql.parser.SqlParseException;
@@ -61,6 +62,10 @@ public interface Planner {
    * @throws org.apache.calcite.tools.RelConversionException if the node
    * cannot be converted or has not been validated
    */
+  RelRoot rel(SqlNode sql) throws RelConversionException;
+
+  /** @deprecated Use {@link #rel}. */
+  @Deprecated // to removed before 2.0
   RelNode convert(SqlNode sql) throws RelConversionException;
 
   /** Returns the type factory. */