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/02/08 18:25:06 UTC

[7/9] incubator-calcite git commit: [CALCITE-88] Add collation as a trait and a kind of RelNode metadata

[CALCITE-88] Add collation as a trait and a kind of RelNode metadata

[CALCITE-526] Add EnumerableMergeJoin, which exploits sorted inputs

[CALCITE-71] Provide a way to declare that tables are sorted

[CALCITE-576] Make RelCollation trait and AbstractRelNode.getCollationList consistent

[CALCITE-581] Add LogicalSort relational expression, and make Sort abstract

[CALCITE-254] Propagate RelCollation on aliased columns in JoinRule

[CALCITE-569] ArrayIndexOutOfBoundsException when deducing collation

More efficient algorithm to check for cycles in the tree of equivalence sets.

FilterJoinRule now propagates traits.

Ord.zip returns random-access list.

Replace references to "Bug#upgrade" with "Deprecated // to be removed before ..."

Add composite traits (RelCompositeTrait); subsets only ever have a simple trait, but other RelNodes can have multiple traits, and appear in each subset that those traits. Each composite trait is canonized within its trait definition, and each of the component traits.

Rename RelTraitSet.subsumes and RelTrait.subsumes to satisfies.


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

Branch: refs/heads/master
Commit: 2709896eb176c14605b1ddc4be57d916ebeb0fe8
Parents: c0120dd
Author: Julian Hyde <jh...@apache.org>
Authored: Sat Dec 13 23:49:02 2014 -0800
Committer: Julian Hyde <jh...@apache.org>
Committed: Sun Feb 8 00:45:51 2015 -0800

----------------------------------------------------------------------
 .../calcite/adapter/clone/ArrayTable.java       |  12 +-
 .../adapter/enumerable/EnumerableCalc.java      |  46 ++-
 .../adapter/enumerable/EnumerableCalcRule.java  |  14 +-
 .../enumerable/EnumerableConvention.java        |   2 +-
 .../adapter/enumerable/EnumerableFilter.java    |  25 ++
 .../enumerable/EnumerableFilterToCalcRule.java  |  15 +-
 .../adapter/enumerable/EnumerableJoin.java      |  20 ++
 .../adapter/enumerable/EnumerableLimit.java     |  29 +-
 .../adapter/enumerable/EnumerableMergeJoin.java | 150 +++++++++
 .../enumerable/EnumerableMergeJoinRule.java     | 116 +++++++
 .../enumerable/EnumerableProjectToCalcRule.java |  15 +-
 .../adapter/enumerable/EnumerableRules.java     |   3 +
 .../adapter/enumerable/EnumerableSort.java      |  22 +-
 .../adapter/enumerable/EnumerableSortRule.java  |   7 +-
 .../adapter/enumerable/EnumerableValues.java    |  25 +-
 .../enumerable/EnumerableValuesRule.java        |   7 +-
 .../adapter/enumerable/PhysTypeImpl.java        |   3 +-
 .../calcite/interpreter/BindableConvention.java |   2 +-
 .../interpreter/InterpretableConvention.java    |   2 +-
 .../apache/calcite/interpreter/SortNode.java    |  37 +--
 .../org/apache/calcite/plan/Convention.java     |   2 +-
 .../apache/calcite/plan/RelCompositeTrait.java  | 149 +++++++++
 .../apache/calcite/plan/RelMultipleTrait.java   |  32 ++
 .../calcite/plan/RelOptAbstractTable.java       |   2 +-
 .../org/apache/calcite/plan/RelOptCluster.java  |  14 +-
 .../calcite/plan/RelOptMaterialization.java     |   4 +-
 .../org/apache/calcite/plan/RelOptUtil.java     | 108 ++-----
 .../java/org/apache/calcite/plan/RelTrait.java  |  20 +-
 .../org/apache/calcite/plan/RelTraitDef.java    |  37 ++-
 .../org/apache/calcite/plan/RelTraitSet.java    | 138 ++++++++-
 .../calcite/plan/SubstitutionVisitor.java       |  14 +-
 .../calcite/plan/volcano/AbstractConverter.java |   3 +-
 .../org/apache/calcite/plan/volcano/RelSet.java |  10 +-
 .../apache/calcite/plan/volcano/RelSubset.java  |  24 +-
 .../calcite/plan/volcano/VolcanoPlanner.java    |  68 ++--
 .../calcite/prepare/CalcitePrepareImpl.java     |   3 +-
 .../calcite/prepare/LixToRelTranslator.java     |  36 +--
 .../org/apache/calcite/prepare/Prepare.java     |  12 +-
 .../calcite/prepare/QueryableRelBuilder.java    |  10 +-
 .../apache/calcite/prepare/RelOptTableImpl.java |  26 +-
 .../org/apache/calcite/rel/AbstractRelNode.java |   2 +
 .../org/apache/calcite/rel/RelCollation.java    |   4 +-
 .../apache/calcite/rel/RelCollationImpl.java    | 110 ++-----
 .../calcite/rel/RelCollationTraitDef.java       |  10 +-
 .../org/apache/calcite/rel/RelCollations.java   | 166 ++++++++++
 .../apache/calcite/rel/RelFieldCollation.java   |  35 ++-
 .../java/org/apache/calcite/rel/RelNode.java    |   2 +-
 .../org/apache/calcite/rel/core/Aggregate.java  |   4 +-
 .../java/org/apache/calcite/rel/core/Calc.java  |  67 ++--
 .../org/apache/calcite/rel/core/Correlate.java  |   8 +-
 .../org/apache/calcite/rel/core/JoinInfo.java   |   5 +
 .../org/apache/calcite/rel/core/Project.java    |  34 +-
 .../apache/calcite/rel/core/RelFactories.java   |  15 +-
 .../org/apache/calcite/rel/core/Window.java     |   4 +-
 .../apache/calcite/rel/externalize/RelJson.java |   3 +-
 .../calcite/rel/logical/LogicalAggregate.java   |  40 ++-
 .../apache/calcite/rel/logical/LogicalCalc.java |  39 ++-
 .../calcite/rel/logical/LogicalCorrelate.java   |  38 ++-
 .../calcite/rel/logical/LogicalFilter.java      |  39 ++-
 .../calcite/rel/logical/LogicalIntersect.java   |  29 +-
 .../apache/calcite/rel/logical/LogicalJoin.java |  75 ++---
 .../calcite/rel/logical/LogicalMinus.java       |  38 ++-
 .../calcite/rel/logical/LogicalProject.java     |  71 +++--
 .../apache/calcite/rel/logical/LogicalSort.java |   2 +
 .../rel/logical/LogicalTableFunctionScan.java   |  32 +-
 .../calcite/rel/logical/LogicalTableModify.java |  49 +--
 .../calcite/rel/logical/LogicalTableScan.java   |  23 +-
 .../calcite/rel/logical/LogicalUnion.java       |  33 +-
 .../calcite/rel/logical/LogicalValues.java      |  44 ++-
 .../calcite/rel/metadata/BuiltInMetadata.java   |   9 +
 .../metadata/DefaultRelMetadataProvider.java    |   3 +-
 .../calcite/rel/metadata/RelMdCollation.java    | 308 +++++++++++++++++++
 .../calcite/rel/metadata/RelMetadataQuery.java  |  18 ++
 .../AggregateExpandDistinctAggregatesRule.java  |   5 +-
 .../AggregateProjectPullUpConstantsRule.java    |  20 +-
 .../rel/rules/AggregateReduceFunctionsRule.java |  12 +-
 .../rel/rules/AggregateUnionAggregateRule.java  |   8 +-
 .../rel/rules/AggregateUnionTransposeRule.java  |  10 +-
 .../calcite/rel/rules/CalcRelSplitter.java      |   8 +-
 .../calcite/rel/rules/FilterCalcMergeRule.java  |  10 +-
 .../calcite/rel/rules/FilterJoinRule.java       |   3 +-
 .../rules/FilterTableFunctionTransposeRule.java |   9 +-
 .../calcite/rel/rules/FilterToCalcRule.java     |  13 +-
 .../calcite/rel/rules/JoinToCorrelateRule.java  |   4 +-
 .../calcite/rel/rules/ProjectCalcMergeRule.java |  18 +-
 .../calcite/rel/rules/ProjectRemoveRule.java    |  15 +-
 .../calcite/rel/rules/ProjectToCalcRule.java    |  15 +-
 .../calcite/rel/rules/ProjectToWindowRule.java  |  18 +-
 .../calcite/rel/rules/PruneEmptyRules.java      |   6 +-
 .../calcite/rel/rules/ReduceDecimalsRule.java   |  13 +-
 .../rel/rules/ReduceExpressionsRule.java        |  13 +-
 .../rel/rules/SortProjectTransposeRule.java     |  16 +-
 .../calcite/rel/rules/UnionMergeRule.java       |   7 +-
 .../calcite/rel/rules/UnionToDistinctRule.java  |   6 +-
 .../calcite/rel/rules/ValuesReduceRule.java     |   2 +-
 .../java/org/apache/calcite/rex/RexNode.java    |   2 +-
 .../java/org/apache/calcite/rex/RexOver.java    |   4 +-
 .../java/org/apache/calcite/rex/RexProgram.java |  28 +-
 .../java/org/apache/calcite/rex/RexUtil.java    |  14 +-
 .../org/apache/calcite/schema/Statistic.java    |   6 +
 .../org/apache/calcite/schema/Statistics.java   |  17 +
 .../java/org/apache/calcite/sql/SqlWindow.java  |   3 +-
 .../apache/calcite/sql/advise/SqlAdvisor.java   |   2 +-
 .../sql/fun/SqlLiteralChainOperator.java        |   3 +-
 .../org/apache/calcite/sql/util/SqlShuttle.java |   5 +-
 .../apache/calcite/sql2rel/RelDecorrelator.java |  70 ++---
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |  16 +-
 .../sql2rel/RelStructuredTypeFlattener.java     |  36 +--
 .../calcite/sql2rel/SqlToRelConverter.java      | 126 ++------
 .../java/org/apache/calcite/tools/Programs.java |   5 +-
 .../main/java/org/apache/calcite/util/Bug.java  |   4 +
 .../org/apache/calcite/util/BuiltInMethod.java  |  28 +-
 .../apache/calcite/util/ImmutableBitSet.java    |   5 +
 .../apache/calcite/util/ImmutableIntList.java   |   2 +-
 .../main/java/org/apache/calcite/util/Util.java |  21 +-
 .../org/apache/calcite/plan/RelWriterTest.java  |  10 +-
 .../plan/volcano/VolcanoPlannerTraitTest.java   |  15 +-
 .../apache/calcite/rel/RelCollationTest.java    |  74 +++++
 .../calcite/sql/parser/SqlParserTest.java       |  16 +
 .../org/apache/calcite/test/CalciteAssert.java  |  15 +-
 .../org/apache/calcite/test/CalciteSuite.java   |   2 +
 .../test/JdbcFrontJdbcBackLinqMiddleTest.java   |   2 +-
 .../calcite/test/JdbcFrontLinqBackTest.java     |  11 +-
 .../java/org/apache/calcite/test/JdbcTest.java  |  66 +++-
 .../org/apache/calcite/test/LatticeTest.java    |  26 ++
 .../apache/calcite/test/MockCatalogReader.java  |  10 +-
 .../apache/calcite/test/RelMetadataTest.java    | 158 ++++++++++
 .../apache/calcite/test/ScannableTableTest.java |  10 +-
 .../apache/calcite/test/SqlToRelTestBase.java   |  15 +-
 .../apache/calcite/tools/FrameworksTest.java    |   6 +-
 .../org/apache/calcite/tools/PlannerTest.java   |   8 +-
 .../calcite/util/ImmutableBitSetTest.java       |  16 +
 core/src/test/resources/sql/sort.oq             |  71 +++++
 .../java/org/apache/calcite/linq4j/Ord.java     |  65 +++-
 .../calcite/adapter/mongodb/MongoRules.java     |   4 +-
 .../adapter/splunk/SplunkPushDownRule.java      |   9 +-
 136 files changed, 2729 insertions(+), 1131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java b/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java
index 5d1c58a..b9bdba8 100644
--- a/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/clone/ArrayTable.java
@@ -25,6 +25,9 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.QueryProvider;
 import org.apache.calcite.linq4j.Queryable;
 import org.apache.calcite.linq4j.tree.Primitive;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelProtoDataType;
@@ -77,7 +80,14 @@ class ArrayTable extends AbstractQueryableTable implements ScannableTable {
         keys.add(ImmutableBitSet.of(ord.i));
       }
     }
-    return Statistics.of(content.size, keys);
+    final List<RelCollation> collations;
+    if (content.sortField >= 0) {
+      collations = ImmutableList.of(
+          RelCollations.of(new RelFieldCollation(content.sortField)));
+    } else {
+      collations = ImmutableList.of();
+    }
+    return Statistics.of(content.size, keys, collations);
   }
 
   public Enumerable<Object[]> scan(DataContext root) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
index b93256d..967d4dd 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java
@@ -30,12 +30,16 @@ import org.apache.calcite.linq4j.tree.Types;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.Pair;
+import org.apache.calcite.util.Util;
 
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
 
 import java.lang.reflect.Modifier;
@@ -50,22 +54,50 @@ import static org.apache.calcite.adapter.enumerable.EnumUtils.NO_PARAMS;
 /** Implementation of {@link org.apache.calcite.rel.core.Calc} in
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableCalc extends Calc implements EnumerableRel {
+  /**
+   * Creates an EnumerableCalc.
+   *
+   * <p>Use {@link #create} unless you know what you're doing.
+   */
+  public EnumerableCalc(RelOptCluster cluster,
+      RelTraitSet traitSet,
+      RelNode input,
+      RexProgram program) {
+    super(cluster, traitSet, input, program);
+    assert getConvention() instanceof EnumerableConvention;
+    assert !program.containsAggs();
+  }
+
+  @Deprecated // to be removed before 2.0
   public EnumerableCalc(
       RelOptCluster cluster,
       RelTraitSet traitSet,
-      RelNode child,
+      RelNode input,
       RexProgram program,
       List<RelCollation> collationList) {
-    super(cluster, traitSet, child, program, collationList);
-    assert getConvention() instanceof EnumerableConvention;
-    assert !program.containsAggs();
+    this(cluster, traitSet, input, program);
+    Util.discard(collationList);
+  }
+
+  /** Creates an EnumerableCalc. */
+  public static EnumerableCalc create(final RelNode input,
+      final RexProgram program) {
+    final RelOptCluster cluster = input.getCluster();
+    final RelTraitSet traitSet = cluster.traitSet()
+        .replace(EnumerableConvention.INSTANCE)
+        .replaceIf(RelCollationTraitDef.INSTANCE,
+            new Supplier<List<RelCollation>>() {
+              public List<RelCollation> get() {
+                return RelMdCollation.calc(input, program);
+              }
+            });
+    return new EnumerableCalc(cluster, traitSet, input, program);
   }
 
   @Override public EnumerableCalc copy(RelTraitSet traitSet, RelNode child,
-      RexProgram program, List<RelCollation> collationList) {
+      RexProgram program) {
     // we do not need to copy program; it is immutable
-    return new EnumerableCalc(getCluster(), traitSet, child,
-        program, collationList);
+    return new EnumerableCalc(getCluster(), traitSet, child, program);
   }
 
   public Result implement(EnumerableRelImplementor implementor, Prefer pref) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalcRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalcRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalcRule.java
index 793dcf8..b94ff57 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalcRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalcRule.java
@@ -36,15 +36,11 @@ class EnumerableCalcRule extends ConverterRule {
 
   public RelNode convert(RelNode rel) {
     final LogicalCalc calc = (LogicalCalc) rel;
-    return new EnumerableCalc(
-        rel.getCluster(),
-        rel.getTraitSet().replace(EnumerableConvention.INSTANCE),
-        convert(
-            calc.getInput(),
-            calc.getInput().getTraitSet()
-                .replace(EnumerableConvention.INSTANCE)),
-        calc.getProgram(),
-        calc.getCollationList());
+    final RelNode input = calc.getInput();
+    return EnumerableCalc.create(
+        convert(input,
+            input.getTraitSet().replace(EnumerableConvention.INSTANCE)),
+        calc.getProgram());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java
index ac1533a..946afae 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableConvention.java
@@ -49,7 +49,7 @@ public enum EnumerableConvention implements Convention {
     return ConventionTraitDef.INSTANCE;
   }
 
-  public boolean subsumes(RelTrait trait) {
+  public boolean satisfies(RelTrait trait) {
     return this == trait;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
index ae68216..f4cc5e9 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilter.java
@@ -18,15 +18,25 @@ package org.apache.calcite.adapter.enumerable;
 
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rex.RexNode;
 
+import com.google.common.base.Supplier;
+
+import java.util.List;
+
 /** Implementation of {@link org.apache.calcite.rel.core.Filter} in
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableFilter
     extends Filter
     implements EnumerableRel {
+  /** Creates an EnumerableFilter.
+   *
+   * <p>Use {@link #create} unless you know what you're doing. */
   public EnumerableFilter(
       RelOptCluster cluster,
       RelTraitSet traitSet,
@@ -36,6 +46,21 @@ public class EnumerableFilter
     assert getConvention() instanceof EnumerableConvention;
   }
 
+  /** Creates an EnumerableFilter. */
+  public static EnumerableFilter create(final RelNode input,
+      RexNode condition) {
+    final RelOptCluster cluster = input.getCluster();
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(EnumerableConvention.INSTANCE)
+            .replaceIf(RelCollationTraitDef.INSTANCE,
+                new Supplier<List<RelCollation>>() {
+                public List<RelCollation> get() {
+                  return RelMdCollation.filter(input);
+                }
+              });
+    return new EnumerableFilter(cluster, traitSet, input, condition);
+  }
+
   public EnumerableFilter copy(RelTraitSet traitSet, RelNode input,
       RexNode condition) {
     return new EnumerableFilter(getCluster(), traitSet, input, condition);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilterToCalcRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilterToCalcRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilterToCalcRule.java
index 09ad78b..9a61124 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilterToCalcRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableFilterToCalcRule.java
@@ -18,15 +18,12 @@ package org.apache.calcite.adapter.enumerable;
 
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.rex.RexProgramBuilder;
 
-import com.google.common.collect.ImmutableList;
-
 /** Variant of {@link org.apache.calcite.rel.rules.FilterToCalcRule} for
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableFilterToCalcRule extends RelOptRule {
@@ -36,24 +33,18 @@ public class EnumerableFilterToCalcRule extends RelOptRule {
 
   public void onMatch(RelOptRuleCall call) {
     final EnumerableFilter filter = call.rel(0);
-    final RelNode rel = filter.getInput();
+    final RelNode input = filter.getInput();
 
     // Create a program containing a filter.
     final RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
-    final RelDataType inputRowType = rel.getRowType();
+    final RelDataType inputRowType = input.getRowType();
     final RexProgramBuilder programBuilder =
         new RexProgramBuilder(inputRowType, rexBuilder);
     programBuilder.addIdentity();
     programBuilder.addCondition(filter.getCondition());
     final RexProgram program = programBuilder.getProgram();
 
-    final EnumerableCalc calc =
-        new EnumerableCalc(
-            filter.getCluster(),
-            filter.getTraitSet(),
-            rel,
-            program,
-            ImmutableList.<RelCollation>of());
+    final EnumerableCalc calc = EnumerableCalc.create(input, program);
     call.transformTo(calc);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
index ef93c17..052979f 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableJoin.java
@@ -42,6 +42,9 @@ import java.util.Set;
 /** Implementation of {@link org.apache.calcite.rel.core.Join} in
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableJoin extends EquiJoin implements EnumerableRel {
+  /** Creates an EnumerableJoin.
+   *
+   * <p>Use {@link #create} unless you know what you're doing. */
   EnumerableJoin(
       RelOptCluster cluster,
       RelTraitSet traits,
@@ -65,6 +68,23 @@ public class EnumerableJoin extends EquiJoin implements EnumerableRel {
         variablesStopped);
   }
 
+  /** Creates an EnumerableJoin. */
+  public static EnumerableJoin create(
+      RelNode left,
+      RelNode right,
+      RexNode condition,
+      ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys,
+      JoinRelType joinType,
+      Set<String> variablesStopped)
+      throws InvalidRelException {
+    final RelOptCluster cluster = left.getCluster();
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(EnumerableConvention.INSTANCE);
+    return new EnumerableJoin(cluster, traitSet, left, right, condition,
+        leftKeys, rightKeys, joinType, variablesStopped);
+  }
+
   @Override public EnumerableJoin copy(RelTraitSet traitSet, RexNode condition,
       RelNode left, RelNode right, JoinRelType joinType,
       boolean semiJoinDone) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
index bdbeaad..2bef8c8 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableLimit.java
@@ -21,13 +21,18 @@ import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.BuiltInMethod;
 
+import com.google.common.base.Supplier;
+
 import java.util.List;
 
 /** Relational expression that applies a limit and/or offset to its input. */
@@ -35,17 +40,35 @@ public class EnumerableLimit extends SingleRel implements EnumerableRel {
   private final RexNode offset;
   private final RexNode fetch;
 
+  /** Creates an EnumerableLimit.
+   *
+   * <p>Use {@link #create} unless you know what you're doing. */
   public EnumerableLimit(
       RelOptCluster cluster,
       RelTraitSet traitSet,
-      RelNode child,
+      RelNode input,
       RexNode offset,
       RexNode fetch) {
-    super(cluster, traitSet, child);
+    super(cluster, traitSet, input);
     this.offset = offset;
     this.fetch = fetch;
     assert getConvention() instanceof EnumerableConvention;
-    assert getConvention() == child.getConvention();
+    assert getConvention() == input.getConvention();
+  }
+
+  /** Creates an EnumerableLimit. */
+  public static EnumerableLimit create(final RelNode input, RexNode offset,
+      RexNode fetch) {
+    final RelOptCluster cluster = input.getCluster();
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(EnumerableConvention.INSTANCE)
+            .replaceIf(RelCollationTraitDef.INSTANCE,
+                new Supplier<List<RelCollation>>() {
+                  public List<RelCollation> get() {
+                    return RelMdCollation.filter(input);
+                  }
+                });
+    return new EnumerableLimit(cluster, traitSet, input, offset, fetch);
   }
 
   @Override public EnumerableLimit copy(

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
new file mode 100644
index 0000000..c370eb9
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoin.java
@@ -0,0 +1,150 @@
+/*
+ * 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.adapter.enumerable;
+
+import org.apache.calcite.linq4j.tree.BlockBuilder;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.InvalidRelException;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
+import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.JoinInfo;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.metadata.RelMdCollation;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.rules.EquiJoin;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.ImmutableIntList;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.List;
+import java.util.Set;
+
+/** Implementation of {@link org.apache.calcite.rel.core.Join} in
+ * {@link EnumerableConvention enumerable calling convention} using
+ * a merge algorithm. */
+public class EnumerableMergeJoin extends EquiJoin implements EnumerableRel {
+  EnumerableMergeJoin(
+      RelOptCluster cluster,
+      RelTraitSet traits,
+      RelNode left,
+      RelNode right,
+      RexNode condition,
+      ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys,
+      JoinRelType joinType,
+      Set<String> variablesStopped)
+      throws InvalidRelException {
+    super(cluster, traits, left, right, condition, leftKeys, rightKeys,
+        joinType, variablesStopped);
+    final List<RelCollation> collations =
+        traits.getTraits(RelCollationTraitDef.INSTANCE);
+    assert collations == null || RelCollations.contains(collations, leftKeys);
+  }
+
+  public static EnumerableMergeJoin create(RelNode left, RelNode right,
+      RexLiteral condition, ImmutableIntList leftKeys,
+      ImmutableIntList rightKeys, JoinRelType joinType)
+      throws InvalidRelException {
+    final RelOptCluster cluster = right.getCluster();
+    RelTraitSet traitSet = cluster.traitSet();
+    if (traitSet.isEnabled(RelCollationTraitDef.INSTANCE)) {
+      final List<RelCollation> collations =
+          RelMdCollation.mergeJoin(left, right, leftKeys, rightKeys);
+      traitSet = traitSet.replace(collations);
+    }
+    return new EnumerableMergeJoin(cluster, traitSet, left, right, condition,
+        leftKeys, rightKeys, joinType, ImmutableSet.<String>of());
+  }
+
+  @Override public EnumerableMergeJoin copy(RelTraitSet traitSet,
+      RexNode condition, RelNode left, RelNode right, JoinRelType joinType,
+      boolean semiJoinDone) {
+    final JoinInfo joinInfo = JoinInfo.of(left, right, condition);
+    assert joinInfo.isEqui();
+    try {
+      return new EnumerableMergeJoin(getCluster(), traitSet, left, right,
+          condition, joinInfo.leftKeys, joinInfo.rightKeys, joinType,
+          variablesStopped);
+    } catch (InvalidRelException e) {
+      // Semantic error not possible. Must be a bug. Convert to
+      // internal error.
+      throw new AssertionError(e);
+    }
+  }
+
+  @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+    // We assume that the inputs are sorted. The price of sorting them has
+    // already been paid. The cost of the join is therefore proportional to the
+    // input and output size.
+    final double rightRowCount = right.getRows();
+    final double leftRowCount = left.getRows();
+    final double rowCount = RelMetadataQuery.getRowCount(this);
+    final double d = leftRowCount + rightRowCount + rowCount;
+    return planner.getCostFactory().makeCost(d, 0, 0);
+  }
+
+  public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+    BlockBuilder builder = new BlockBuilder();
+    final Result leftResult =
+        implementor.visitChild(this, 0, (EnumerableRel) left, pref);
+    Expression leftExpression =
+        builder.append(
+            "left", leftResult.block);
+    final Result rightResult =
+        implementor.visitChild(this, 1, (EnumerableRel) right, pref);
+    Expression rightExpression =
+        builder.append(
+            "right", rightResult.block);
+    final PhysType physType =
+        PhysTypeImpl.of(
+            implementor.getTypeFactory(), getRowType(), pref.preferArray());
+    final PhysType keyPhysType =
+        leftResult.physType.project(
+            leftKeys, JavaRowFormat.LIST);
+    return implementor.result(
+        physType,
+        builder.append(
+            Expressions.call(
+                BuiltInMethod.MERGE_JOIN.method,
+                Expressions.list(
+                    leftExpression,
+                    rightExpression,
+                    leftResult.physType.generateAccessor(leftKeys),
+                    rightResult.physType.generateAccessor(rightKeys),
+                    EnumUtils.joinSelector(joinType,
+                        physType,
+                        ImmutableList.of(
+                            leftResult.physType, rightResult.physType)),
+                    Expressions.constant(
+                        joinType.generatesNullsOnLeft()),
+                    Expressions.constant(
+                        joinType.generatesNullsOnRight())))).toBlock());
+  }
+}
+
+// End EnumerableMergeJoin.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
new file mode 100644
index 0000000..51f09f4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableMergeJoinRule.java
@@ -0,0 +1,116 @@
+/*
+ * 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.adapter.enumerable;
+
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.InvalidRelException;
+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.convert.ConverterRule;
+import org.apache.calcite.rel.core.JoinInfo;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.logical.LogicalJoin;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/** Planner rule that converts a
+ * {@link org.apache.calcite.rel.logical.LogicalJoin} relational expression
+ * {@link EnumerableConvention enumerable calling convention}.
+ *
+ * @see org.apache.calcite.adapter.enumerable.EnumerableJoinRule
+ */
+class EnumerableMergeJoinRule extends ConverterRule {
+  EnumerableMergeJoinRule() {
+    super(LogicalJoin.class,
+        Convention.NONE,
+        EnumerableConvention.INSTANCE,
+        "EnumerableMergeJoinRule");
+  }
+
+  @Override public RelNode convert(RelNode rel) {
+    LogicalJoin join = (LogicalJoin) rel;
+    final JoinInfo info =
+        JoinInfo.of(join.getLeft(), join.getRight(), join.getCondition());
+    if (join.getJoinType() != JoinRelType.INNER) {
+      // EnumerableMergeJoin only supports inner join.
+      // (It supports non-equi join, using a post-filter; see below.)
+      return null;
+    }
+    if (info.pairs().size() == 0) {
+      // EnumerableMergeJoin CAN support cartesian join, but disable it for now.
+      return null;
+    }
+    final List<RelNode> newInputs = Lists.newArrayList();
+    final List<RelCollation> collations = Lists.newArrayList();
+    int offset = 0;
+    for (Ord<RelNode> ord : Ord.zip(join.getInputs())) {
+      RelTraitSet traits = ord.e.getTraitSet()
+          .replace(EnumerableConvention.INSTANCE);
+      if (!info.pairs().isEmpty()) {
+        final List<RelFieldCollation> fieldCollations = Lists.newArrayList();
+        for (int key : info.keys().get(ord.i)) {
+          fieldCollations.add(
+              new RelFieldCollation(key,
+                  RelFieldCollation.Direction.ASCENDING,
+                  RelFieldCollation.NullDirection.LAST));
+        }
+        final RelCollation collation = RelCollations.of(fieldCollations);
+        collations.add(RelCollations.shift(collation, offset));
+        traits = traits.replace(collation);
+      }
+      newInputs.add(convert(ord.e, traits));
+      offset += ord.e.getRowType().getFieldCount();
+    }
+    final RelNode left = newInputs.get(0);
+    final RelNode right = newInputs.get(1);
+    final RelOptCluster cluster = join.getCluster();
+    RelNode newRel;
+    try {
+      RelTraitSet traits = join.getTraitSet()
+          .replace(EnumerableConvention.INSTANCE);
+      if (!collations.isEmpty()) {
+        traits = traits.replace(collations);
+      }
+      newRel = new EnumerableMergeJoin(cluster,
+          traits,
+          left,
+          right,
+          info.getEquiCondition(left, right, cluster.getRexBuilder()),
+          info.leftKeys,
+          info.rightKeys,
+          join.getJoinType(),
+          join.getVariablesStopped());
+    } catch (InvalidRelException e) {
+      EnumerableRules.LOGGER.fine(e.toString());
+      return null;
+    }
+    if (!info.isEqui()) {
+      newRel = new EnumerableFilter(cluster, newRel.getTraitSet(),
+          newRel, info.getRemaining(cluster.getRexBuilder()));
+    }
+    return newRel;
+  }
+}
+
+// End EnumerableMergeJoinRule.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectToCalcRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectToCalcRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectToCalcRule.java
index c654d19..2e1a4a8 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectToCalcRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableProjectToCalcRule.java
@@ -18,12 +18,9 @@ package org.apache.calcite.adapter.enumerable;
 
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rex.RexProgram;
 
-import com.google.common.collect.ImmutableList;
-
 /** Variant of {@link org.apache.calcite.rel.rules.ProjectToCalcRule} for
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableProjectToCalcRule extends RelOptRule {
@@ -33,20 +30,14 @@ public class EnumerableProjectToCalcRule extends RelOptRule {
 
   public void onMatch(RelOptRuleCall call) {
     final EnumerableProject project = call.rel(0);
-    final RelNode child = project.getInput();
+    final RelNode input = project.getInput();
     final RexProgram program =
-        RexProgram.create(child.getRowType(),
+        RexProgram.create(input.getRowType(),
             project.getProjects(),
             null,
             project.getRowType(),
             project.getCluster().getRexBuilder());
-    final EnumerableCalc calc =
-        new EnumerableCalc(
-            project.getCluster(),
-            project.getTraitSet(),
-            child,
-            program,
-            ImmutableList.<RelCollation>of());
+    final EnumerableCalc calc = EnumerableCalc.create(input, program);
     call.transformTo(calc);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRules.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRules.java
index 700e7e2..37fe2de 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRules.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRules.java
@@ -33,6 +33,9 @@ public class EnumerableRules {
   public static final RelOptRule ENUMERABLE_JOIN_RULE =
       new EnumerableJoinRule();
 
+  public static final RelOptRule ENUMERABLE_MERGE_JOIN_RULE =
+      new EnumerableMergeJoinRule();
+
   public static final RelOptRule ENUMERABLE_SEMI_JOIN_RULE =
       new EnumerableSemiJoinRule();
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java
index a20279c..8046d64 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSort.java
@@ -31,11 +31,27 @@ import org.apache.calcite.util.Pair;
 /** Implementation of {@link org.apache.calcite.rel.core.Sort} in
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableSort extends Sort implements EnumerableRel {
+  /**
+   * Creates an EnumerableSort.
+   *
+   * <p>Use {@link #create} unless you know what you're doing.
+   */
   public EnumerableSort(RelOptCluster cluster, RelTraitSet traitSet,
-      RelNode child, RelCollation collation, RexNode offset, RexNode fetch) {
-    super(cluster, traitSet, child, collation, offset, fetch);
+      RelNode input, RelCollation collation, RexNode offset, RexNode fetch) {
+    super(cluster, traitSet, input, collation, offset, fetch);
     assert getConvention() instanceof EnumerableConvention;
-    assert getConvention() == child.getConvention();
+    assert getConvention() == input.getConvention();
+  }
+
+  /** Creates an EnumerableSort. */
+  public static EnumerableSort create(RelNode child, RelCollation collation,
+      RexNode offset, RexNode fetch) {
+    final RelOptCluster cluster = child.getCluster();
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(EnumerableConvention.INSTANCE)
+            .replace(collation);
+    return new EnumerableSort(cluster, traitSet, child, collation, offset,
+        fetch);
   }
 
   @Override public EnumerableSort copy(

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java
index 35a7e33..eb3d737 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableSortRule.java
@@ -17,7 +17,6 @@
 package org.apache.calcite.adapter.enumerable;
 
 import org.apache.calcite.plan.Convention;
-import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.core.Sort;
@@ -37,12 +36,8 @@ class EnumerableSortRule extends ConverterRule {
     if (sort.offset != null || sort.fetch != null) {
       return null;
     }
-    final RelTraitSet traitSet =
-        sort.getTraitSet().replace(EnumerableConvention.INSTANCE);
     final RelNode input = sort.getInput();
-    return new EnumerableSort(
-        rel.getCluster(),
-        traitSet,
+    return EnumerableSort.create(
         convert(
             input,
             input.getTraitSet().replace(EnumerableConvention.INSTANCE)),

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
index e4eeeee..76e08e2 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValues.java
@@ -23,14 +23,18 @@ import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.Pair;
 
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
 
 import java.lang.reflect.Type;
@@ -40,15 +44,30 @@ import java.util.List;
 /** Implementation of {@link org.apache.calcite.rel.core.Values} in
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableValues extends Values implements EnumerableRel {
-  EnumerableValues(RelOptCluster cluster, RelDataType rowType,
+  /** Creates an EnumerableValues. */
+  private EnumerableValues(RelOptCluster cluster, RelDataType rowType,
       ImmutableList<ImmutableList<RexLiteral>> tuples, RelTraitSet traitSet) {
     super(cluster, rowType, tuples, traitSet);
   }
 
+  /** Creates an EnumerableValues. */
+  public static EnumerableValues create(RelOptCluster cluster,
+      final RelDataType rowType,
+      final ImmutableList<ImmutableList<RexLiteral>> tuples) {
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(EnumerableConvention.INSTANCE)
+            .replaceIf(RelCollationTraitDef.INSTANCE,
+                new Supplier<List<RelCollation>>() {
+                  public List<RelCollation> get() {
+                    return RelMdCollation.values(rowType, tuples);
+                  }
+                });
+    return new EnumerableValues(cluster, rowType, tuples, traitSet);
+  }
+
   @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
     assert inputs.isEmpty();
-    return new EnumerableValues(
-        getCluster(), rowType, tuples, traitSet);
+    return create(getCluster(), rowType, tuples);
   }
 
   public Result implement(EnumerableRelImplementor implementor, Prefer pref) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
index ed1b3ca..6e14bcc 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableValuesRule.java
@@ -33,11 +33,8 @@ public class EnumerableValuesRule extends ConverterRule {
 
   @Override public RelNode convert(RelNode rel) {
     LogicalValues values = (LogicalValues) rel;
-    return new EnumerableValues(
-        values.getCluster(),
-        values.getRowType(),
-        values.getTuples(),
-        values.getTraitSet().replace(EnumerableConvention.INSTANCE));
+    return EnumerableValues.create(values.getCluster(), values.getRowType(),
+        values.getTuples());
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
index 59ec646..6d51b97 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/PhysTypeImpl.java
@@ -517,8 +517,7 @@ public class PhysTypeImpl implements PhysType {
           Function1.class,
           Expressions.field(
               null,
-              Collections.class,
-              "EMPTY_LIST"),
+              BuiltInMethod.COMPARABLE_EMPTY_LIST.field),
           v1);
     case 1:
       int field0 = fields.get(0);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java b/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java
index d74745c..46dad77 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/BindableConvention.java
@@ -54,7 +54,7 @@ public enum BindableConvention implements Convention {
     return ConventionTraitDef.INSTANCE;
   }
 
-  public boolean subsumes(RelTrait trait) {
+  public boolean satisfies(RelTrait trait) {
     return this == trait;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/interpreter/InterpretableConvention.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/InterpretableConvention.java b/core/src/main/java/org/apache/calcite/interpreter/InterpretableConvention.java
index fb41d9d..e0e2fde 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/InterpretableConvention.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/InterpretableConvention.java
@@ -48,7 +48,7 @@ public enum InterpretableConvention implements Convention {
     return ConventionTraitDef.INSTANCE;
   }
 
-  public boolean subsumes(RelTrait trait) {
+  public boolean satisfies(RelTrait trait) {
     return this == trait;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/interpreter/SortNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/SortNode.java b/core/src/main/java/org/apache/calcite/interpreter/SortNode.java
index 1e5f07b..e2c0eeb 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/SortNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/SortNode.java
@@ -97,55 +97,28 @@ public class SortNode extends AbstractSingleNode<Sort> {
             }));
   }
 
-  private static int compare(Comparable c1, Comparable c2,
-      int nullComparison) {
-    if (c1 == c2) {
-      return 0;
-    } else if (c1 == null) {
-      return nullComparison;
-    } else if (c2 == null) {
-      return -nullComparison;
-    } else {
-      //noinspection unchecked
-      return c1.compareTo(c2);
-    }
-  }
-
-  private Comparator<Row> comparator(final RelFieldCollation fieldCollation) {
-    final int nullComparison = getNullComparison(fieldCollation.nullDirection);
+  private Comparator<Row> comparator(RelFieldCollation fieldCollation) {
+    final int nullComparison = fieldCollation.nullDirection.nullComparison;
+    final int x = fieldCollation.getFieldIndex();
     switch (fieldCollation.direction) {
     case ASCENDING:
       return new Comparator<Row>() {
         public int compare(Row o1, Row o2) {
-          final int x = fieldCollation.getFieldIndex();
           final Comparable c1 = (Comparable) o1.getValues()[x];
           final Comparable c2 = (Comparable) o2.getValues()[x];
-          return SortNode.compare(c1, c2, nullComparison);
+          return RelFieldCollation.compare(c1, c2, nullComparison);
         }
       };
     default:
       return new Comparator<Row>() {
         public int compare(Row o1, Row o2) {
-          final int x = fieldCollation.getFieldIndex();
           final Comparable c1 = (Comparable) o1.getValues()[x];
           final Comparable c2 = (Comparable) o2.getValues()[x];
-          return SortNode.compare(c2, c1, -nullComparison);
+          return RelFieldCollation.compare(c2, c1, -nullComparison);
         }
       };
     }
   }
-
-  private int getNullComparison(RelFieldCollation.NullDirection nullDirection) {
-    switch (nullDirection) {
-    case FIRST:
-      return -1;
-    case UNSPECIFIED:
-    case LAST:
-      return 1;
-    default:
-      throw new AssertionError(nullDirection);
-    }
-  }
 }
 
 // End SortNode.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/Convention.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/Convention.java b/core/src/main/java/org/apache/calcite/plan/Convention.java
index dbd1a5c..46750c2 100644
--- a/core/src/main/java/org/apache/calcite/plan/Convention.java
+++ b/core/src/main/java/org/apache/calcite/plan/Convention.java
@@ -55,7 +55,7 @@ public interface Convention extends RelTrait {
 
     public void register(RelOptPlanner planner) {}
 
-    public boolean subsumes(RelTrait trait) {
+    public boolean satisfies(RelTrait trait) {
       return this == trait;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java b/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java
new file mode 100644
index 0000000..e2bf62f
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/RelCompositeTrait.java
@@ -0,0 +1,149 @@
+/*
+ * 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.plan;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Ordering;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A trait that consists of a list of traits, all of the same type.
+ *
+ * <p>It exists so that multiple traits of the same type
+ * ({@link org.apache.calcite.plan.RelTraitDef}) can be stored in the same
+ * {@link org.apache.calcite.plan.RelTraitSet}.
+ *
+ * @param <T> Member trait
+ */
+class RelCompositeTrait<T extends RelMultipleTrait> implements RelTrait {
+  private final RelTraitDef traitDef;
+  private final T[] traits;
+
+  /** Creates a RelCompositeTrait. */
+  // Must remain private. Does not copy the array.
+  private RelCompositeTrait(RelTraitDef traitDef, T[] traits) {
+    this.traitDef = traitDef;
+    this.traits = Preconditions.checkNotNull(traits);
+    //noinspection unchecked
+    assert Ordering.natural()
+        .isStrictlyOrdered(Arrays.asList((Comparable[]) traits))
+        : Arrays.toString(traits);
+    for (T trait : traits) {
+      assert trait.getTraitDef() == this.traitDef;
+    }
+  }
+
+  /** Creates a RelCompositeTrait. The constituent traits are canonized. */
+  @SuppressWarnings("unchecked")
+  static <T extends RelMultipleTrait> RelCompositeTrait<T> of(RelTraitDef def,
+      List<T> traitList) {
+    final RelCompositeTrait<T> compositeTrait;
+    if (traitList.isEmpty()) {
+      compositeTrait = new EmptyCompositeTrait<T>(def);
+    } else {
+      final RelMultipleTrait[] traits =
+          traitList.toArray(new RelMultipleTrait[traitList.size()]);
+      for (int i = 0; i < traits.length; i++) {
+        traits[i] = (T) def.canonize(traits[i]);
+      }
+      compositeTrait = new RelCompositeTrait<T>(def, (T[]) traits);
+    }
+    return def.canonizeComposite(compositeTrait);
+  }
+
+  public RelTraitDef getTraitDef() {
+    return traitDef;
+  }
+
+  @Override public int hashCode() {
+    return Arrays.hashCode(traits);
+  }
+
+  @Override public boolean equals(Object obj) {
+    return this == obj
+        || obj instanceof RelCompositeTrait
+        && Arrays.equals(traits, ((RelCompositeTrait) obj).traits);
+  }
+
+  @Override public String toString() {
+    return Arrays.toString(traits);
+  }
+
+  public boolean satisfies(RelTrait trait) {
+    for (T t : traits) {
+      if (t.satisfies(trait)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public void register(RelOptPlanner planner) {
+  }
+
+  /** Returns an immutable list of the traits in this composite trait. */
+  public List<T> traitList() {
+    return ImmutableList.copyOf(traits);
+  }
+
+  RelCompositeTrait<T> canonize(RelTraitDef<T> traitDef) {
+    T[] newTraits = null;
+    for (int i = 0; i < traits.length; i++) {
+      final T trait = traits[i];
+      final T trait2 = traitDef.canonize(trait);
+      if (trait2 != trait) {
+        if (newTraits == null) {
+          newTraits = traits.clone();
+        }
+        newTraits[i] = trait2;
+      }
+    }
+    if (newTraits == null) {
+      return this;
+    }
+    assert false;
+    // TODO: cache duplicate composites
+    return new RelCompositeTrait<T>(traitDef, newTraits);
+  }
+
+  public T trait(int i) {
+    return traits[i];
+  }
+
+  public int size() {
+    return traits.length;
+  }
+
+  /** Composite trait with 0 elements. */
+  private static class EmptyCompositeTrait<T extends RelMultipleTrait>
+      extends RelCompositeTrait<T> {
+    private EmptyCompositeTrait(RelTraitDef traitDef) {
+      //noinspection unchecked
+      super(traitDef, (T[]) new RelMultipleTrait[0]);
+    }
+
+    @Override public boolean satisfies(RelTrait trait) {
+      //noinspection unchecked
+      return ((T) trait).isTop();
+    }
+  }
+}
+
+// End RelCompositeTrait.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelMultipleTrait.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelMultipleTrait.java b/core/src/main/java/org/apache/calcite/plan/RelMultipleTrait.java
new file mode 100644
index 0000000..fbbc32f
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/RelMultipleTrait.java
@@ -0,0 +1,32 @@
+/*
+ * 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.plan;
+
+/**
+ * Trait for which a given relational expression can have multiple values.
+ *
+ * <p>The most common example is sorted-ness (collation). The TIME dimension
+ * table might be sorted by [year, month, date] and also by [time_id].
+ */
+public interface RelMultipleTrait
+    extends RelTrait, Comparable<RelMultipleTrait> {
+  /** Returns whether this trait is satisfied by every instance of the trait
+   * (including itself). */
+  boolean isTop();
+}
+
+// End RelMultipleTrait.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java b/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
index f672ef3..4252a25 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptAbstractTable.java
@@ -89,7 +89,7 @@ public abstract class RelOptAbstractTable implements RelOptTable {
   }
 
   public RelNode toRel(ToRelContext context) {
-    return new LogicalTableScan(context.getCluster(), this);
+    return LogicalTableScan.create(context.getCluster(), this);
   }
 
   public Expression getExpression(Class clazz) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
index d1171ab..2071a8e 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
@@ -108,13 +108,13 @@ public class RelOptCluster {
     return metadataFactory;
   }
 
-  public RelTraitSet traitSetOf(RelTrait... traits) {
-    RelTraitSet traitSet = emptyTraitSet;
-    assert traitSet.size() == planner.getRelTraitDefs().size();
-    for (RelTrait trait : traits) {
-      traitSet = traitSet.replace(trait);
-    }
-    return traitSet;
+  /** Returns the default trait set for this cluster. */
+  public RelTraitSet traitSet() {
+    return emptyTraitSet;
+  }
+
+  public RelTraitSet traitSetOf(RelTrait trait) {
+    return emptyTraitSet.replace(trait);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
index f09e5fd..8c9eaae 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
@@ -149,7 +149,7 @@ public class RelOptMaterialization {
                           Mappings.offsetSource(rightMapping, offset),
                           leftMapping.getTargetCount()));
               final RelNode project = RelOptUtil.createProject(
-                  new LogicalTableScan(cluster, leftRelOptTable),
+                  LogicalTableScan.create(cluster, leftRelOptTable),
                   Mappings.asList(mapping.inverse()));
               final List<RexNode> conditions = Lists.newArrayList();
               if (left.condition != null) {
@@ -173,7 +173,7 @@ public class RelOptMaterialization {
                       Mappings.offsetSource(leftMapping, offset),
                       Mappings.offsetTarget(rightMapping, leftCount));
               final RelNode project = RelOptUtil.createProject(
-                  new LogicalTableScan(cluster, rightRelOptTable),
+                  LogicalTableScan.create(cluster, rightRelOptTable),
                   Mappings.asList(mapping.inverse()));
               final List<RexNode> conditions = Lists.newArrayList();
               if (left.condition != null) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/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 88ad2ff..bbcb75d 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -17,8 +17,6 @@
 package org.apache.calcite.plan;
 
 import org.apache.calcite.linq4j.Ord;
-import org.apache.calcite.rel.RelCollation;
-import org.apache.calcite.rel.RelCollationImpl;
 import org.apache.calcite.rel.RelHomogeneousShuttle;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelShuttle;
@@ -73,7 +71,6 @@ import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.MultisetSqlType;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
-import org.apache.calcite.util.Holder;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Permutation;
@@ -394,7 +391,7 @@ public abstract class RelOptUtil {
               extraName);
 
       ret =
-          new LogicalAggregate(ret.getCluster(), ret, false,
+          LogicalAggregate.create(ret, false,
               ImmutableBitSet.of(), null, ImmutableList.of(aggCall));
     }
 
@@ -435,7 +432,7 @@ public abstract class RelOptUtil {
       final int keyCount = ret.getRowType().getFieldCount();
       if (!needsOuterJoin) {
         return Pair.<RelNode, Boolean>of(
-            new LogicalAggregate(cluster, ret, false,
+            LogicalAggregate.create(ret, false,
                 ImmutableBitSet.range(keyCount), null,
                 ImmutableList.<AggregateCall>of()),
             false);
@@ -470,7 +467,7 @@ public abstract class RelOptUtil {
               null,
               null);
 
-      ret = new LogicalAggregate(cluster, ret, false,
+      ret = LogicalAggregate.create(ret, false,
           ImmutableBitSet.range(projectedKeyCount), null,
           ImmutableList.of(aggCall));
 
@@ -713,7 +710,7 @@ public abstract class RelOptUtil {
               null));
     }
 
-    return new LogicalAggregate(rel.getCluster(), rel, false,
+    return LogicalAggregate.create(rel, false,
         ImmutableBitSet.of(), null, aggCalls);
   }
 
@@ -725,7 +722,7 @@ public abstract class RelOptUtil {
    * @return rel implementing DISTINCT
    */
   public static RelNode createDistinctRel(RelNode rel) {
-    return new LogicalAggregate(rel.getCluster(), rel, false,
+    return LogicalAggregate.create(rel, false,
         ImmutableBitSet.range(rel.getRowType().getFieldCount()), null,
         ImmutableList.<AggregateCall>of());
   }
@@ -2072,54 +2069,6 @@ public abstract class RelOptUtil {
    * @param joinFilters  list of filters to push to the join
    * @param leftFilters  list of filters to push to the left child
    * @param rightFilters list of filters to push to the right child
-   * @param smart        Whether to try to strengthen the join type
-   * @return whether at least one filter was pushed, or join type was
-   * strengthened
-   *
-   * @deprecated Use the other {@link #classifyFilters};
-   * very short-term; will be removed before
-   * {@link org.apache.calcite.util.Bug#upgrade(String) calcite-0.9.2}.
-   */
-  public static boolean classifyFilters(
-      RelNode joinRel,
-      List<RexNode> filters,
-      JoinRelType joinType,
-      boolean pushInto,
-      boolean pushLeft,
-      boolean pushRight,
-      List<RexNode> joinFilters,
-      List<RexNode> leftFilters,
-      List<RexNode> rightFilters,
-      Holder<JoinRelType> joinTypeHolder,
-      boolean smart) {
-    final JoinRelType oldJoinType = joinType;
-    final int filterCount = filters.size();
-    if (smart) {
-      joinType = simplifyJoin(joinRel, ImmutableList.copyOf(joinFilters),
-          joinType);
-      joinTypeHolder.set(joinType);
-    }
-    classifyFilters(joinRel, filters, joinType, pushInto, pushLeft, pushRight,
-        joinFilters, leftFilters, rightFilters);
-
-    return filters.size() < filterCount || joinType != oldJoinType;
-  }
-
-  /**
-   * Classifies filters according to where they should be processed. They
-   * either stay where they are, are pushed to the join (if they originated
-   * from above the join), or are pushed to one of the children. Filters that
-   * are pushed are added to list passed in as input parameters.
-   *
-   * @param joinRel      join node
-   * @param filters      filters to be classified
-   * @param joinType     join type
-   * @param pushInto     whether filters can be pushed into the ON clause
-   * @param pushLeft     true if filters can be pushed to the left
-   * @param pushRight    true if filters can be pushed to the right
-   * @param joinFilters  list of filters to push to the join
-   * @param leftFilters  list of filters to push to the left child
-   * @param rightFilters list of filters to push to the right child
    * @return whether at least one filter was pushed
    */
   public static boolean classifyFilters(
@@ -2496,8 +2445,7 @@ public abstract class RelOptUtil {
       nodes.add(new RexInputRef(source, field.getType()));
       names.add(field.getName());
     }
-    return new LogicalProject(
-        child.getCluster(), child, nodes, names);
+    return LogicalProject.create(child, nodes, names);
   }
 
   /** Returns whether relational expression {@code target} occurs within a
@@ -2673,38 +2621,27 @@ public abstract class RelOptUtil {
       List<String> fieldNames,
       boolean optimize) {
     final RelOptCluster cluster = child.getCluster();
-    final RexProgram program =
-        RexProgram.create(
-            child.getRowType(), exprs, null, fieldNames,
-            cluster.getRexBuilder());
-    final List<RelCollation> collationList =
-        program.getCollations(child.getCollationList());
-    final RelDataType rowType =
-        RexUtil.createStructType(
-            cluster.getTypeFactory(),
-            exprs,
-            fieldNames == null
-                ? null
-                : SqlValidatorUtil.uniquify(
-                    fieldNames, SqlValidatorUtil.F_SUGGESTER));
+    final List<String> fieldNames2 =
+        fieldNames == null
+            ? null
+            : SqlValidatorUtil.uniquify(fieldNames,
+                SqlValidatorUtil.F_SUGGESTER);
     if (optimize
         && ProjectRemoveRule.isIdentity(exprs, child.getRowType())) {
       if (child instanceof Project && fieldNames != null) {
+        final RelDataType rowType =
+            RexUtil.createStructType(
+                cluster.getTypeFactory(),
+                exprs,
+                fieldNames2);
         // Rename columns of child projection if desired field names are given.
         Project childProject = (Project) child;
         child = childProject.copy(childProject.getTraitSet(),
-            childProject.getInput(), childProject.getProjects(),
-            rowType);
+            childProject.getInput(), childProject.getProjects(), rowType);
       }
       return child;
     }
-    return new LogicalProject(cluster,
-        cluster.traitSetOf(collationList.isEmpty()
-            ? RelCollationImpl.EMPTY
-            : collationList.get(0)),
-        child,
-        exprs,
-        rowType);
+    return LogicalProject.create(child, exprs, fieldNames2);
   }
 
   /**
@@ -2813,12 +2750,7 @@ public abstract class RelOptUtil {
             rel.getCluster().getTypeFactory().createStructType(
                 outputTypeList,
                 outputNameList));
-    return new LogicalCalc(
-        rel.getCluster(),
-        rel.getTraitSet(),
-        rel,
-        program,
-        ImmutableList.<RelCollation>of());
+    return LogicalCalc.create(rel, program);
   }
 
   /**
@@ -3079,7 +3011,7 @@ public abstract class RelOptUtil {
      */
     public static ImmutableBitSet bits(List<RexNode> exprs, RexNode expr) {
       final InputFinder inputFinder = new InputFinder();
-      RexProgram.apply(inputFinder, exprs, expr);
+      RexUtil.apply(inputFinder, exprs, expr);
       return inputFinder.inputBitSet.build();
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelTrait.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelTrait.java b/core/src/main/java/org/apache/calcite/plan/RelTrait.java
index ccbd963..e611e95 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelTrait.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelTrait.java
@@ -52,21 +52,23 @@ public interface RelTrait {
   boolean equals(Object o);
 
   /**
-   * Returns whether this trait subsumes a given trait.
+   * Returns whether this trait satisfies a given trait.
    *
-   * <p>Must form a partial order: must be reflective (t subsumes t),
-   * anti-symmetric (if t1 subsumes t2 and t1 != t2 then t2 does not subsume
-   * t1),
-   * and transitive (if t1 subsumes t2 and t2 subsumes t3, then t1 subsumes
-   * t3)</p>
+   * <p>A trait satisfies another if it is the same or stricter. For example,
+   * {@code ORDER BY x, y} satisfies {@code ORDER BY x}.
    *
-   * <p>Many traits cannot be substituted, in which case, this method should
-   * return {@code equals(trait)}.</p>
+   * <p>A trait's {@code satisfies} relation must be a partial order (reflexive,
+   * anti-symmetric, transitive). Many traits cannot be "loosened"; their
+   * {@code satisfies} is an equivalence relation, where only X satisfies X.
+   *
+   * <p>If a trait has multiple values
+   * (see {@link org.apache.calcite.plan.RelCompositeTrait})
+   * a collection (T0, T1, ...) satisfies T if any Ti satisfies T.
    *
    * @param trait Given trait
    * @return Whether this trait subsumes a given trait
    */
-  boolean subsumes(RelTrait trait);
+  boolean satisfies(RelTrait trait);
 
   /**
    * Returns a succinct name for this trait. The planner may use this String

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java b/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java
index 0273568..1f26b38 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelTraitDef.java
@@ -23,6 +23,9 @@ import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 
+import java.util.List;
+import javax.annotation.Nonnull;
+
 /**
  * RelTraitDef represents a class of {@link RelTrait}s. Implementations of
  * RelTraitDef may be singletons under the following conditions:
@@ -58,11 +61,35 @@ public abstract class RelTraitDef<T extends RelTrait> {
           .softValues()
           .build(
               new CacheLoader<T, T>() {
-                @Override public T load(T key) throws Exception {
+                @Override public T load(@Nonnull T key) throws Exception {
                   return key;
                 }
               });
 
+  /** Cache of composite traits.
+   *
+   * <p>Uses soft values to allow GC.
+   *
+   * <p>You can look up using a {@link RelCompositeTrait} whose constituent
+   * traits are not canonized.
+   */
+  private final LoadingCache<Object, RelCompositeTrait> canonicalCompositeMap =
+      CacheBuilder.newBuilder()
+          .softValues()
+          .build(
+              new CacheLoader<Object, RelCompositeTrait>() {
+                @Override public RelCompositeTrait load(@Nonnull Object key) {
+                  if (key instanceof RelCompositeTrait) {
+                    return (RelCompositeTrait) key;
+                  }
+                  @SuppressWarnings("unchecked")
+                  final List<RelMultipleTrait> list =
+                      (List<RelMultipleTrait>) key;
+                  final RelTraitDef def = list.get(0).getTraitDef();
+                  return (RelCompositeTrait) RelCompositeTrait.of(def, list);
+                }
+              });
+
   //~ Constructors -----------------------------------------------------------
 
   protected RelTraitDef() {
@@ -104,6 +131,10 @@ public abstract class RelTraitDef<T extends RelTrait> {
    * @return a canonical RelTrait.
    */
   public final T canonize(T trait) {
+    if (trait instanceof RelCompositeTrait) {
+      RelCompositeTrait relCompositeTrait = (RelCompositeTrait) trait;
+      return (T) canonizeComposite(relCompositeTrait);
+    }
     assert getTraitClass().isInstance(trait)
         : getClass().getName()
         + " cannot canonize a "
@@ -112,6 +143,10 @@ public abstract class RelTraitDef<T extends RelTrait> {
     return canonicalMap.getUnchecked(trait);
   }
 
+  final RelCompositeTrait canonizeComposite(RelCompositeTrait compositeTrait) {
+    return canonicalCompositeMap.getUnchecked(compositeTrait);
+  }
+
   /**
    * Converts the given RelNode to the given RelTrait.
    *

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java b/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java
index d055a40..bc80ea1 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelTraitSet.java
@@ -19,6 +19,7 @@ package org.apache.calcite.plan;
 import org.apache.calcite.runtime.FlatLists;
 import org.apache.calcite.util.Pair;
 
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
 
 import java.util.AbstractList;
@@ -81,11 +82,37 @@ public final class RelTraitSet extends AbstractList<RelTrait> {
     return traits[index];
   }
 
+  /**
+   * Retrieves a list of traits from the set.
+   *
+   * @param index 0-based index into ordered RelTraitSet
+   * @return the RelTrait
+   * @throws ArrayIndexOutOfBoundsException if index greater than or equal to
+   *                                        {@link #size()} or less than 0.
+   */
+  public <E extends RelMultipleTrait> List<E> getTraits(int index) {
+    final RelTrait trait = traits[index];
+    if (trait instanceof RelCompositeTrait) {
+      //noinspection unchecked
+      return ((RelCompositeTrait<E>) trait).traitList();
+    } else {
+      //noinspection unchecked
+      return ImmutableList.of((E) trait);
+    }
+  }
+
   public RelTrait get(int index) {
     return getTrait(index);
   }
 
   /**
+   * Returns whether a given kind of trait is enabled.
+   */
+  public <T extends RelTrait> boolean isEnabled(RelTraitDef<T> traitDef) {
+    return getTrait(traitDef) != null;
+  }
+
+  /**
    * Retrieves a RelTrait of the given type from the set.
    *
    * @param traitDef the type of RelTrait to retrieve
@@ -102,6 +129,25 @@ public final class RelTraitSet extends AbstractList<RelTrait> {
   }
 
   /**
+   * Retrieves a list of traits of the given type from the set.
+   *
+   * <p>Only valid for traits that support multiple entries. (E.g. collation.)
+   *
+   * @param traitDef the type of RelTrait to retrieve
+   * @return the RelTrait, or null if not found
+   */
+  public <T extends RelMultipleTrait> List<T> getTraits(
+      RelTraitDef<T> traitDef) {
+    int index = findIndex(traitDef);
+    if (index >= 0) {
+      //noinspection unchecked
+      return (List<T>) getTraits(index);
+    }
+
+    return null;
+  }
+
+  /**
    * Replaces an existing RelTrait in the set.
    * Returns a different trait set; does not modify this trait set.
    *
@@ -144,6 +190,38 @@ public final class RelTraitSet extends AbstractList<RelTrait> {
     return replace(index, trait);
   }
 
+  /** Replaces the trait(s) of a given type with a list of traits of the same
+   * type.
+   *
+   * <p>The list must not be empty, and all traits must be of the same type.
+   */
+  public <T extends RelMultipleTrait> RelTraitSet replace(List<T> traits) {
+    assert !traits.isEmpty();
+    final RelTraitDef def = traits.get(0).getTraitDef();
+    return replace(RelCompositeTrait.of(def, traits));
+  }
+
+  /** Replaces the trait(s) of a given type with a list of traits of the same
+   * type.
+   *
+   * <p>The list must not be empty, and all traits must be of the same type.
+   */
+  public <T extends RelMultipleTrait> RelTraitSet replace(RelTraitDef<T> def,
+      List<T> traits) {
+    return replace(RelCompositeTrait.of(def, traits));
+  }
+
+  /** If a given trait is enabled, replaces it by calling the given function. */
+  public <T extends RelMultipleTrait> RelTraitSet replaceIf(RelTraitDef<T> def,
+      Supplier<List<T>> traitSupplier) {
+    int index = findIndex(def);
+    if (index < 0) {
+      return this; // trait is not enabled; ignore it
+    }
+    final List<T> traitList = traitSupplier.get();
+    return replace(index, RelCompositeTrait.of(def, traitList));
+  }
+
   /**
    * Returns the size of the RelTraitSet.
    *
@@ -166,6 +244,12 @@ public final class RelTraitSet extends AbstractList<RelTrait> {
       return null;
     }
 
+    if (trait instanceof RelCompositeTrait) {
+      // Composite traits are canonized on creation
+      //noinspection unchecked
+      return (T) trait;
+    }
+
     //noinspection unchecked
     return (T) trait.getTraitDef().canonize(trait);
   }
@@ -187,24 +271,29 @@ public final class RelTraitSet extends AbstractList<RelTrait> {
   }
 
   /**
-   * Returns whether this trait set subsumes another trait set.
+   * Returns whether this trait set satisfies another trait set.
    *
-   * <p>For that to happen, each trait subsumes the corresponding trait in the
-   * other set. In particular, each trait set subsumes itself, because each
-   * trait subsumes itself.</p>
+   * <p>For that to happen, each trait satisfies the corresponding trait in the
+   * other set. In particular, each trait set satisfies itself, because each
+   * trait subsumes itself.
    *
    * <p>Intuitively, if a relational expression is needed that has trait set
-   * S, and trait set S1 subsumes S, then a relational expression R in S1
-   * meets that need. For example, if we need a relational expression that has
+   * S (A, B), and trait set S1 (A1, B1) subsumes S, then any relational
+   * expression R in S1 meets that need.
+   *
+   * <p>For example, if we need a relational expression that has
    * trait set S = {enumerable convention, sorted on [C1 asc]}, and R
-   * has {enumerable convention, sorted on [C1 asc, C2]}</p>
+   * has {enumerable convention, sorted on [C3], [C1, C2]}. R has two
+   * sort keys, but one them [C1, C2] satisfies S [C1], and that is enough.
    *
    * @param that another RelTraitSet
-   * @return whether this trait set subsumes other trait set
+   * @return whether this trait set satisfies other trait set
+   *
+   * @see org.apache.calcite.plan.RelTrait#satisfies(RelTrait)
    */
-  public boolean subsumes(RelTraitSet that) {
+  public boolean satisfies(RelTraitSet that) {
     for (Pair<RelTrait, RelTrait> pair : Pair.zip(traits, that.traits)) {
-      if (!pair.left.subsumes(pair.right)) {
+      if (!pair.left.satisfies(pair.right)) {
         return false;
       }
     }
@@ -393,6 +482,35 @@ public final class RelTraitSet extends AbstractList<RelTrait> {
     return builder.build();
   }
 
+  /** Returns whether there are any composite traits in this set. */
+  public boolean allSimple() {
+    for (RelTrait trait : traits) {
+      if (trait instanceof RelCompositeTrait) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /** Returns a trait set similar to this one but with all composite traits
+   * flattened. */
+  public RelTraitSet simplify() {
+    RelTraitSet x = this;
+    for (int i = 0; i < traits.length; i++) {
+      final RelTrait trait = traits[i];
+      if (trait instanceof RelCompositeTrait) {
+        //noinspection unchecked
+        final RelCompositeTrait<RelMultipleTrait> compositeTrait =
+            (RelCompositeTrait<RelMultipleTrait>) trait;
+        x = x.replace(i,
+            compositeTrait.size() == 0
+                ?  trait.getTraitDef().getDefault()
+                : compositeTrait.trait(0));
+      }
+    }
+    return x;
+  }
+
   /** Cache of trait sets. */
   private static class Cache {
     final Map<List<RelTrait>, RelTraitSet> map =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/2709896e/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index 97e2605..e0ad056 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -20,7 +20,6 @@ import org.apache.calcite.avatica.util.Spaces;
 import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelCollation;
-import org.apache.calcite.rel.RelCollationImpl;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.core.Aggregate;
@@ -590,17 +589,15 @@ public class SubstitutionVisitor {
       return ((MutableLeafRel) node).rel;
     case PROJECT:
       final MutableProject project = (MutableProject) node;
-      return new LogicalProject(node.cluster,
-          node.cluster.traitSetOf(RelCollationImpl.EMPTY),
-          fromMutable(project.input),
+      return LogicalProject.create(fromMutable(project.input),
           project.projects, project.rowType);
     case FILTER:
       final MutableFilter filter = (MutableFilter) node;
-      return new LogicalFilter(node.cluster, fromMutable(filter.input),
+      return LogicalFilter.create(fromMutable(filter.input),
           filter.condition);
     case AGGREGATE:
       final MutableAggregate aggregate = (MutableAggregate) node;
-      return new LogicalAggregate(node.cluster, fromMutable(aggregate.input),
+      return LogicalAggregate.create(fromMutable(aggregate.input),
           aggregate.indicator, aggregate.groupSet, aggregate.groupSets,
           aggregate.aggCalls);
     case SORT:
@@ -609,8 +606,7 @@ public class SubstitutionVisitor {
           sort.offset, sort.fetch);
     case UNION:
       final MutableUnion union = (MutableUnion) node;
-      return new LogicalUnion(union.cluster, fromMutables(union.inputs),
-          union.all);
+      return LogicalUnion.create(fromMutables(union.inputs), union.all);
     default:
       throw new AssertionError(node.deep());
     }
@@ -2114,7 +2110,7 @@ public class SubstitutionVisitor {
           cluster.getRexBuilder().makeInputRef(newProject,
               newProjects.size() - 1);
 
-      call.transformTo(new LogicalFilter(cluster, newProject, newCondition));
+      call.transformTo(LogicalFilter.create(newProject, newCondition));
     }
   }
 }