calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jh...@apache.org
Subject [2/2] incubator-calcite git commit: [CALCITE-445] Pull up filters rejected by a ProjectableFilterableTable
Date Sun, 22 Feb 2015 18:56:48 GMT
[CALCITE-445] Pull up filters rejected by a ProjectableFilterableTable

Add "create" methods for more relational expressions.

Add EnumerableTableScanRule, rather than RelOptTableImpl.toRel jumping straight to an implementation; for ScannableTable, FilterableTable or ProjectableFilterableTable, use BindableTableScanRule, ProjectTableScanRule, FilterTableScanRule, which generate BindableTableScan.

Add streaming syntax, and get basic streaming query working.


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

Branch: refs/heads/master
Commit: bd0b606178252c90805aff49851f4c9d5eea1a0a
Parents: 1ae40b0
Author: Julian Hyde <jhyde@apache.org>
Authored: Wed Jan 28 01:57:34 2015 -0800
Committer: julianhyde <jhyde@apache.org>
Committed: Sat Feb 21 20:46:51 2015 -0800

----------------------------------------------------------------------
 .../enumerable/EnumerableInterpreter.java       |  20 +-
 .../enumerable/EnumerableInterpreterRule.java   |  44 ++++
 .../adapter/enumerable/EnumerableProject.java   |  31 +++
 .../adapter/enumerable/EnumerableRel.java       |  10 +-
 .../adapter/enumerable/EnumerableRules.java     |   3 +
 .../EnumerableTableFunctionScanRule.java        |   2 +-
 .../adapter/enumerable/EnumerableTableScan.java |  65 +++++-
 .../enumerable/EnumerableTableScanRule.java     |  52 +++++
 .../apache/calcite/interpreter/Bindables.java   | 135 ++++++++++--
 .../apache/calcite/interpreter/FilterNode.java  |   2 +-
 .../apache/calcite/interpreter/Interpreter.java |  51 ++++-
 .../calcite/interpreter/JaninoRexCompiler.java  |  10 +-
 .../apache/calcite/interpreter/JoinNode.java    |   2 +-
 .../org/apache/calcite/interpreter/Nodes.java   | 123 +----------
 .../apache/calcite/interpreter/ProjectNode.java |   3 +-
 .../calcite/interpreter/TableScanNode.java      | 218 ++++++++++++++-----
 .../apache/calcite/interpreter/ValuesNode.java  |   4 +-
 .../org/apache/calcite/model/ModelHandler.java  |   3 +
 .../calcite/prepare/CalcitePrepareImpl.java     |  14 +-
 .../apache/calcite/prepare/RelOptTableImpl.java |  95 +++-----
 .../org/apache/calcite/rel/core/TableScan.java  |  17 +-
 .../calcite/rel/logical/LogicalTableScan.java   |  28 ++-
 .../calcite/rel/logical/LogicalWindow.java      |   2 +
 .../apache/calcite/rel/rules/CalcSplitRule.java |  59 +++++
 .../calcite/rel/rules/FilterTableRule.java      | 166 --------------
 .../calcite/rel/rules/FilterTableScanRule.java  | 122 +++++++++++
 .../calcite/rel/rules/ProjectTableRule.java     | 166 --------------
 .../calcite/rel/rules/ProjectTableScanRule.java | 125 +++++++++++
 .../java/org/apache/calcite/schema/Schemas.java |  27 ++-
 .../java/org/apache/calcite/tools/Programs.java |   4 +-
 .../main/java/org/apache/calcite/util/Bug.java  |  24 +-
 .../org/apache/calcite/util/BuiltInMethod.java  |  11 +-
 .../apache/calcite/util/ImmutableIntList.java   |  15 +-
 .../org/apache/calcite/test/CalciteAssert.java  |  43 ++--
 .../apache/calcite/test/InterpreterTest.java    |  26 +--
 .../java/org/apache/calcite/test/JdbcTest.java  |   4 +-
 .../java/org/apache/calcite/test/ModelTest.java |  18 +-
 .../apache/calcite/test/ScannableTableTest.java |  11 +-
 .../calcite/test/TableInRootSchemaTest.java     |   8 +-
 .../apache/calcite/tools/FrameworksTest.java    |   4 +-
 .../java/org/apache/calcite/util/UtilTest.java  |  19 +-
 .../java/org/apache/calcite/test/CsvTest.java   |   3 +-
 42 files changed, 1062 insertions(+), 727 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreter.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreter.java
index 988464e..cfa241f 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreter.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreter.java
@@ -45,17 +45,33 @@ public class EnumerableInterpreter extends SingleRel
   /**
    * Creates an EnumerableInterpreter.
    *
+   * <p>Use {@link #create} unless you know what you're doing.
+   *
    * @param cluster Cluster
    * @param traitSet Traits
    * @param input Input relation
    * @param factor Cost multiply factor
    */
-  public EnumerableInterpreter(RelOptCluster cluster,
-      RelTraitSet traitSet, RelNode input, double factor) {
+  public EnumerableInterpreter(RelOptCluster cluster, RelTraitSet traitSet,
+      RelNode input, double factor) {
     super(cluster, traitSet, input);
+    assert getConvention() instanceof EnumerableConvention;
     this.factor = factor;
   }
 
+  /**
+   * Creates an EnumerableInterpreter.
+   *
+   * @param input Input relation
+   * @param factor Cost multiply factor
+   */
+  public static EnumerableInterpreter create(RelNode input, double factor) {
+    final RelTraitSet traitSet = input.getTraitSet()
+        .replace(EnumerableConvention.INSTANCE);
+    return new EnumerableInterpreter(input.getCluster(), traitSet, input,
+        factor);
+  }
+
   @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
     return super.computeSelfCost(planner).multiplyBy(factor);
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreterRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreterRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreterRule.java
new file mode 100644
index 0000000..99996fa
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableInterpreterRule.java
@@ -0,0 +1,44 @@
+/*
+ * 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.interpreter.BindableConvention;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+
+/**
+ * Planner rule that converts {@link org.apache.calcite.interpreter.BindableRel}
+ * to {@link org.apache.calcite.adapter.enumerable.EnumerableRel} by creating
+ * an {@link org.apache.calcite.adapter.enumerable.EnumerableInterpreter}.
+ */
+public class EnumerableInterpreterRule extends ConverterRule {
+  public static final EnumerableInterpreterRule INSTANCE =
+      new EnumerableInterpreterRule();
+
+  private EnumerableInterpreterRule() {
+    super(RelNode.class, BindableConvention.INSTANCE,
+        EnumerableConvention.INSTANCE, "EnumerableInterpreterRule");
+  }
+
+  //~ Methods ----------------------------------------------------------------
+
+  @Override public RelNode convert(RelNode rel) {
+    return EnumerableInterpreter.create(rel, 0.5d);
+  }
+}
+
+// End EnumerableInterpreterRule.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/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 d1f116a..9ab0fb2 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
@@ -18,17 +18,33 @@ 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.Project;
+import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.Util;
 
+import com.google.common.base.Supplier;
+
 import java.util.List;
 
 /** Implementation of {@link org.apache.calcite.rel.core.Project} in
  * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention enumerable calling convention}. */
 public class EnumerableProject extends Project implements EnumerableRel {
+  /**
+   * Creates an EnumerableProject.
+   *
+   * <p>Use {@link #create} unless you know what you're doing.
+   *
+   * @param cluster  Cluster this relational expression belongs to
+   * @param traitSet Traits of this relational expression
+   * @param input    Input relational expression
+   * @param projects List of expressions for the input columns
+   * @param rowType  Output row type
+   */
   public EnumerableProject(
       RelOptCluster cluster,
       RelTraitSet traitSet,
@@ -47,6 +63,21 @@ public class EnumerableProject extends Project implements EnumerableRel {
     Util.discard(flags);
   }
 
+  /** Creates a LogicalProject, 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();
+    final RelTraitSet traitSet =
+        cluster.traitSet().replace(EnumerableConvention.INSTANCE)
+            .replaceIfs(RelCollationTraitDef.INSTANCE,
+                new Supplier<List<RelCollation>>() {
+                  public List<RelCollation> get() {
+                    return RelMdCollation.project(input, projects);
+                  }
+                });
+    return new EnumerableProject(cluster, traitSet, input, projects, rowType);
+  }
+
   public EnumerableProject copy(RelTraitSet traitSet, RelNode input,
       List<RexNode> projects, RelDataType rowType) {
     return new EnumerableProject(getCluster(), traitSet, input,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java
index c005faa..94d13c7 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableRel.java
@@ -37,23 +37,21 @@ public interface EnumerableRel
   RelFactories.FilterFactory FILTER_FACTORY =
       new RelFactories.FilterFactory() {
         public RelNode createFilter(RelNode child, RexNode condition) {
-          return new EnumerableFilter(child.getCluster(),
-              child.getTraitSet(), child, condition);
+          return EnumerableFilter.create(child, condition);
         }
       };
 
   RelFactories.ProjectFactory PROJECT_FACTORY =
       new RelFactories.ProjectFactory() {
         public RelNode createProject(RelNode child,
-            List<? extends RexNode> exprs, List<String> fieldNames) {
+            List<? extends RexNode> projects, List<String> fieldNames) {
           final RelOptCluster cluster = child.getCluster();
           final RelDataType rowType =
-              RexUtil.createStructType(cluster.getTypeFactory(), exprs,
+              RexUtil.createStructType(cluster.getTypeFactory(), projects,
                   fieldNames == null ? null
                       : SqlValidatorUtil.uniquify(fieldNames,
                           SqlValidatorUtil.F_SUGGESTER));
-          return new EnumerableProject(cluster,
-              child.getTraitSet(), child, exprs, rowType);
+          return EnumerableProject.create(child, projects, rowType);
         }
       };
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/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 37fe2de..289bc68 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
@@ -93,6 +93,9 @@ public class EnumerableRules {
   public static final EnumerableProjectToCalcRule
   ENUMERABLE_PROJECT_TO_CALC_RULE = new EnumerableProjectToCalcRule();
 
+  public static final EnumerableTableScanRule ENUMERABLE_TABLE_SCAN_RULE =
+      new EnumerableTableScanRule();
+
   public static final EnumerableTableFunctionScanRule
   ENUMERABLE_TABLE_FUNCTION_SCAN_RULE = new EnumerableTableFunctionScanRule();
 }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScanRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScanRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScanRule.java
index b22c528..5d06c18 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScanRule.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableFunctionScanRule.java
@@ -29,7 +29,7 @@ import org.apache.calcite.rel.logical.LogicalTableFunctionScan;
 public class EnumerableTableFunctionScanRule extends ConverterRule {
   public EnumerableTableFunctionScanRule() {
     super(LogicalTableFunctionScan.class, Convention.NONE,
-        EnumerableConvention.INSTANCE, "EnumerableTableFunctionRule");
+        EnumerableConvention.INSTANCE, "EnumerableTableFunctionScanRule");
   }
 
   @Override public RelNode convert(RelNode rel) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java
index bd11411..ddd3212 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScan.java
@@ -29,13 +29,20 @@ import org.apache.calcite.linq4j.tree.Types;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptTable;
 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.TableScan;
 import org.apache.calcite.schema.FilterableTable;
 import org.apache.calcite.schema.ProjectableFilterableTable;
+import org.apache.calcite.schema.QueryableTable;
 import org.apache.calcite.schema.ScannableTable;
+import org.apache.calcite.schema.Table;
 import org.apache.calcite.util.BuiltInMethod;
 
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+
 import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.List;
@@ -47,6 +54,9 @@ public class EnumerableTableScan
     implements EnumerableRel {
   private final Class elementType;
 
+  /** Creates an EnumerableTableScan.
+   *
+   * <p>Use {@link #create} unless you know what you are doing. */
   public EnumerableTableScan(RelOptCluster cluster, RelTraitSet traitSet,
       RelOptTable table, Class elementType) {
     super(cluster, traitSet, table);
@@ -54,12 +64,58 @@ public class EnumerableTableScan
     this.elementType = elementType;
   }
 
+  /** Creates an EnumerableTableScan. */
+  public static EnumerableTableScan create(RelOptCluster cluster,
+      RelOptTable relOptTable) {
+    final Table table = relOptTable.unwrap(Table.class);
+    Class elementType = EnumerableTableScan.deduceElementType(table);
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(EnumerableConvention.INSTANCE)
+            .replaceIfs(RelCollationTraitDef.INSTANCE,
+                new Supplier<List<RelCollation>>() {
+                  public List<RelCollation> get() {
+                    if (table != null) {
+                      return table.getStatistic().getCollations();
+                    }
+                    return ImmutableList.of();
+                  }
+                });
+    return new EnumerableTableScan(cluster, traitSet, relOptTable, elementType);
+  }
+
+  /** Returns whether EnumerableTableScan can generate code to handle a
+   * particular variant of the Table SPI. */
+  public static boolean canHandle(Table table) {
+    // FilterableTable and ProjectableFilterableTable cannot be handled in
+    // enumerable convention because they might reject filters and those filters
+    // would need to be handled dynamically.
+    return table instanceof QueryableTable
+        || table instanceof ScannableTable;
+  }
+
+  public static Class deduceElementType(Table table) {
+    if (table instanceof QueryableTable) {
+      final QueryableTable queryableTable = (QueryableTable) table;
+      final Type type = queryableTable.getElementType();
+      if (type instanceof Class) {
+        return (Class) type;
+      } else {
+        return Object[].class;
+      }
+    } else if (table instanceof ScannableTable
+        || table instanceof FilterableTable
+        || table instanceof ProjectableFilterableTable) {
+      return Object[].class;
+    } else {
+      return Object.class;
+    }
+  }
+
   private Expression getExpression(PhysType physType) {
     final Expression expression = table.getExpression(Queryable.class);
     final Expression expression2 = toEnumerable(expression);
     assert Types.isAssignableFrom(Enumerable.class, expression2.getType());
-    Expression expression3 = toRows(physType, expression2);
-    return expression3;
+    return toRows(physType, expression2);
   }
 
   private Expression toEnumerable(Expression expression) {
@@ -100,7 +156,7 @@ public class EnumerableTableScan
     final ParameterExpression row_ =
         Expressions.parameter(elementType, "row");
     final int fieldCount = table.getRowType().getFieldCount();
-    List<Expression> expressionList = new ArrayList<Expression>(fieldCount);
+    List<Expression> expressionList = new ArrayList<>(fieldCount);
     for (int i = 0; i < fieldCount; i++) {
       expressionList.add(
           oldFormat.field(row_, i, physType.getJavaFieldType(i)));
@@ -131,8 +187,7 @@ public class EnumerableTableScan
   }
 
   @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
-    return new EnumerableTableScan(getCluster(), traitSet, table,
-        elementType);
+    return new EnumerableTableScan(getCluster(), traitSet, table, elementType);
   }
 
   public Result implement(EnumerableRelImplementor implementor, Prefer pref) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java
new file mode 100644
index 0000000..416c01a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableScanRule.java
@@ -0,0 +1,52 @@
+/*
+ * 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.Expression;
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+import org.apache.calcite.rel.logical.LogicalTableScan;
+import org.apache.calcite.schema.Table;
+
+/** Planner rule that converts a
+ * {@link org.apache.calcite.rel.logical.LogicalTableFunctionScan}
+ * relational expression
+ * {@link EnumerableConvention enumerable calling convention}. */
+public class EnumerableTableScanRule extends ConverterRule {
+  public EnumerableTableScanRule() {
+    super(LogicalTableScan.class, Convention.NONE,
+        EnumerableConvention.INSTANCE, "EnumerableTableScanRule");
+  }
+
+  @Override public RelNode convert(RelNode rel) {
+    LogicalTableScan scan = (LogicalTableScan) rel;
+    final RelOptTable relOptTable = scan.getTable();
+    final Table table = relOptTable.unwrap(Table.class);
+    if (!EnumerableTableScan.canHandle(table)) {
+      return null;
+    }
+    final Expression expression = relOptTable.getExpression(Object.class);
+    if (expression == null) {
+      return null;
+    }
+    return EnumerableTableScan.create(scan.getCluster(), relOptTable);
+  }
+}
+
+// End EnumerableTableScanRule.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
index 0e63b55..a4474b4 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/Bindables.java
@@ -31,7 +31,9 @@ import org.apache.calcite.plan.RelOptUtil;
 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.RelNode;
+import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.convert.ConverterRule;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
@@ -48,15 +50,25 @@ import org.apache.calcite.rel.logical.LogicalAggregate;
 import org.apache.calcite.rel.logical.LogicalFilter;
 import org.apache.calcite.rel.logical.LogicalJoin;
 import org.apache.calcite.rel.logical.LogicalProject;
+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.logical.LogicalWindow;
+import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.FilterableTable;
+import org.apache.calcite.schema.ProjectableFilterableTable;
 import org.apache.calcite.schema.ScannableTable;
+import org.apache.calcite.schema.Table;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.ImmutableIntList;
 
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
 
 import java.util.List;
@@ -68,7 +80,7 @@ import java.util.Set;
 public class Bindables {
   private Bindables() {}
 
-  public static final RelOptRule BINDABLE_TABLE_RULE =
+  public static final RelOptRule BINDABLE_TABLE_SCAN_RULE =
       new BindableTableScanRule();
 
   public static final RelOptRule BINDABLE_FILTER_RULE =
@@ -99,7 +111,7 @@ public class Bindables {
   public static final ImmutableList<RelOptRule> RULES =
       ImmutableList.of(
           NoneToBindableConverterRule.INSTANCE,
-          BINDABLE_TABLE_RULE,
+          BINDABLE_TABLE_SCAN_RULE,
           BINDABLE_FILTER_RULE,
           BINDABLE_PROJECT_RULE,
           BINDABLE_SORT_RULE,
@@ -120,35 +132,102 @@ public class Bindables {
     return new Interpreter(dataContext, rel);
   }
 
-  /** Rule that converts a {@link ScannableTable} to bindable convention. */
+  /** Rule that converts a {@link org.apache.calcite.rel.core.TableScan}
+   * to bindable convention. */
   private static class BindableTableScanRule extends RelOptRule {
-    public BindableTableScanRule() {
-      super(operand(TableScan.class, none()));
+    private BindableTableScanRule() {
+      super(operand(LogicalTableScan.class, none()));
     }
 
     @Override public void onMatch(RelOptRuleCall call) {
-      final TableScan scan = call.rel(0);
-      call.transformTo(
-          new BindableTableScan(scan.getCluster(),
-              scan.getTraitSet().replace(BindableConvention.INSTANCE),
-              scan.getTable()));
+      final LogicalTableScan scan = call.rel(0);
+      final RelOptTable table = scan.getTable();
+      if (BindableTableScan.canHandle(table)) {
+        call.transformTo(
+            BindableTableScan.create(scan.getCluster(), table));
+      }
     }
   }
 
   /** Scan of a table that implements {@link ScannableTable} and therefore can
    * be converted into an {@link Enumerable}. */
-  private static class BindableTableScan
+  public static class BindableTableScan
       extends TableScan implements BindableRel {
+    public final ImmutableList<RexNode> filters;
+    public final ImmutableIntList projects;
+
+    /** Creates a BindableTableScan.
+     *
+     * <p>Use {@link #create} unless you know what you are doing. */
     BindableTableScan(RelOptCluster cluster, RelTraitSet traitSet,
-        RelOptTable table) {
+        RelOptTable table, ImmutableList<RexNode> filters,
+        ImmutableIntList projects) {
       super(cluster, traitSet, table);
+      this.filters = Preconditions.checkNotNull(filters);
+      this.projects = Preconditions.checkNotNull(projects);
+      Preconditions.checkArgument(canHandle(table));
+    }
+
+    /** Creates a BindableTableScan. */
+    public static BindableTableScan create(RelOptCluster cluster,
+        RelOptTable relOptTable) {
+      return create(cluster, relOptTable, ImmutableList.<RexNode>of(),
+          identity(relOptTable));
+    }
+
+    /** Creates a BindableTableScan. */
+    public static BindableTableScan create(RelOptCluster cluster,
+        RelOptTable relOptTable, List<RexNode> filters,
+        List<Integer> projects) {
+      final Table table = relOptTable.unwrap(Table.class);
+      final RelTraitSet traitSet =
+          cluster.traitSetOf(BindableConvention.INSTANCE)
+              .replaceIfs(RelCollationTraitDef.INSTANCE,
+                  new Supplier<List<RelCollation>>() {
+                    public List<RelCollation> get() {
+                      if (table != null) {
+                        return table.getStatistic().getCollations();
+                      }
+                      return ImmutableList.of();
+                    }
+                  });
+      return new BindableTableScan(cluster, traitSet, relOptTable,
+          ImmutableList.copyOf(filters), ImmutableIntList.copyOf(projects));
+    }
+
+    @Override public RelDataType deriveRowType() {
+      final RelDataTypeFactory.FieldInfoBuilder builder =
+          getCluster().getTypeFactory().builder();
+      final List<RelDataTypeField> fieldList =
+          table.getRowType().getFieldList();
+      for (int project : projects) {
+        builder.add(fieldList.get(project));
+      }
+      return builder.build();
     }
 
     public Class<Object[]> getElementType() {
       return Object[].class;
     }
 
+    @Override public RelWriter explainTerms(RelWriter pw) {
+      return super.explainTerms(pw)
+          .itemIf("filters", filters, !filters.isEmpty())
+          .itemIf("projects", projects, !projects.equals(identity()));
+    }
+
+    @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+      return super.computeSelfCost(planner).multiplyBy(0.01d);
+    }
+
+    public static boolean canHandle(RelOptTable table) {
+      return table.unwrap(ScannableTable.class) != null
+          || table.unwrap(FilterableTable.class) != null
+          || table.unwrap(ProjectableFilterableTable.class) != null;
+    }
+
     public Enumerable<Object[]> bind(DataContext dataContext) {
+      // TODO: filterable and projectable
       return table.unwrap(ScannableTable.class).scan(dataContext);
     }
 
@@ -166,8 +245,7 @@ public class Bindables {
 
     public RelNode convert(RelNode rel) {
       final LogicalFilter filter = (LogicalFilter) rel;
-      return new BindableFilter(rel.getCluster(),
-          rel.getTraitSet().replace(BindableConvention.INSTANCE),
+      return BindableFilter.create(
           convert(filter.getInput(),
               filter.getInput().getTraitSet()
                   .replace(BindableConvention.INSTANCE)),
@@ -184,6 +262,21 @@ public class Bindables {
       assert getConvention() instanceof BindableConvention;
     }
 
+    /** Creates a BindableFilter. */
+    public static BindableFilter create(final RelNode input,
+        RexNode condition) {
+      final RelOptCluster cluster = input.getCluster();
+      final RelTraitSet traitSet =
+          cluster.traitSetOf(BindableConvention.INSTANCE)
+              .replaceIfs(RelCollationTraitDef.INSTANCE,
+                  new Supplier<List<RelCollation>>() {
+                    public List<RelCollation> get() {
+                      return RelMdCollation.filter(input);
+                    }
+                  });
+      return new BindableFilter(cluster, traitSet, input, condition);
+    }
+
     public BindableFilter copy(RelTraitSet traitSet, RelNode input,
         RexNode condition) {
       return new BindableFilter(getCluster(), traitSet, input, condition);
@@ -207,7 +300,7 @@ public class Bindables {
    * to a {@link BindableProject}.
    */
   private static class BindableProjectRule extends ConverterRule {
-    BindableProjectRule() {
+    private BindableProjectRule() {
       super(LogicalProject.class, RelOptUtil.PROJECT_PREDICATE, Convention.NONE,
           BindableConvention.INSTANCE, "BindableProjectRule");
     }
@@ -257,7 +350,7 @@ public class Bindables {
    * {@link org.apache.calcite.interpreter.Bindables.BindableSort}.
    */
   private static class BindableSortRule extends ConverterRule {
-    BindableSortRule() {
+    private BindableSortRule() {
       super(Sort.class, Convention.NONE, BindableConvention.INSTANCE,
           "BindableSortRule");
     }
@@ -308,7 +401,7 @@ public class Bindables {
    * to a {@link BindableJoin}.
    */
   private static class BindableJoinRule extends ConverterRule {
-    BindableJoinRule() {
+    private BindableJoinRule() {
       super(LogicalJoin.class, Convention.NONE, BindableConvention.INSTANCE,
           "BindableJoinRule");
     }
@@ -363,7 +456,7 @@ public class Bindables {
    * to a {@link BindableUnion}.
    */
   private static class BindableUnionRule extends ConverterRule {
-    BindableUnionRule() {
+    private BindableUnionRule() {
       super(LogicalUnion.class, Convention.NONE, BindableConvention.INSTANCE,
           "BindableUnionRule");
     }
@@ -431,7 +524,7 @@ public class Bindables {
 
   /** Rule that converts a {@link Values} to bindable convention. */
   private static class BindableValuesRule extends ConverterRule {
-    BindableValuesRule() {
+    private BindableValuesRule() {
       super(LogicalValues.class, Convention.NONE, BindableConvention.INSTANCE,
           "BindableValuesRule");
     }
@@ -502,7 +595,7 @@ public class Bindables {
 
   /** Rule that converts an {@link Aggregate} to bindable convention. */
   private static class BindableAggregateRule extends ConverterRule {
-    BindableAggregateRule() {
+    private BindableAggregateRule() {
       super(LogicalAggregate.class, Convention.NONE,
           BindableConvention.INSTANCE, "BindableAggregateRule");
     }
@@ -559,7 +652,7 @@ public class Bindables {
    * to a {@link BindableWindow}.
    */
   private static class BindableWindowRule extends ConverterRule {
-    BindableWindowRule() {
+    private BindableWindowRule() {
       super(LogicalWindow.class, Convention.NONE, BindableConvention.INSTANCE,
           "BindableWindowRule");
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/FilterNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/FilterNode.java b/core/src/main/java/org/apache/calcite/interpreter/FilterNode.java
index e06e12d..7d4ab3d 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/FilterNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/FilterNode.java
@@ -32,7 +32,7 @@ public class FilterNode extends AbstractSingleNode<Filter> {
     super(interpreter, rel);
     this.condition =
         interpreter.compile(ImmutableList.of(rel.getCondition()),
-            rel.getInputs());
+            rel.getRowType());
     this.context = interpreter.createContext();
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java b/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java
index 93f18ef..deaf061 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/Interpreter.java
@@ -19,9 +19,17 @@ package org.apache.calcite.interpreter;
 import org.apache.calcite.DataContext;
 import org.apache.calcite.linq4j.AbstractEnumerable;
 import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.plan.hep.HepPlanner;
+import org.apache.calcite.plan.hep.HepProgram;
+import org.apache.calcite.plan.hep.HepProgramBuilder;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelVisitor;
+import org.apache.calcite.rel.rules.CalcSplitRule;
+import org.apache.calcite.rel.rules.FilterTableScanRule;
+import org.apache.calcite.rel.rules.ProjectTableScanRule;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
@@ -31,6 +39,7 @@ import org.apache.calcite.util.ReflectUtil;
 import org.apache.calcite.util.ReflectiveVisitDispatcher;
 import org.apache.calcite.util.ReflectiveVisitor;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -55,11 +64,25 @@ public class Interpreter extends AbstractEnumerable<Object[]> {
   protected final ScalarCompiler scalarCompiler;
 
   public Interpreter(DataContext dataContext, RelNode rootRel) {
-    this.dataContext = dataContext;
+    this.dataContext = Preconditions.checkNotNull(dataContext);
     this.scalarCompiler =
         new JaninoRexCompiler(rootRel.getCluster().getRexBuilder());
-    Compiler compiler = new Nodes.CoreCompiler(this);
-    this.rootRel = compiler.visitRoot(rootRel);
+    final RelNode rel = optimize(rootRel);
+    final Compiler compiler = new Nodes.CoreCompiler(this);
+    this.rootRel = compiler.visitRoot(rel);
+  }
+
+  private RelNode optimize(RelNode rootRel) {
+    final HepProgram hepProgram = new HepProgramBuilder()
+        .addRuleInstance(CalcSplitRule.INSTANCE)
+        .addRuleInstance(FilterTableScanRule.INSTANCE)
+        .addRuleInstance(FilterTableScanRule.INTERPRETER)
+        .addRuleInstance(ProjectTableScanRule.INSTANCE)
+        .addRuleInstance(ProjectTableScanRule.INTERPRETER).build();
+    final HepPlanner planner = new HepPlanner(hepProgram);
+    planner.setRoot(rootRel);
+    rootRel = planner.findBestExp();
+    return rootRel;
   }
 
   public Enumerator<Object[]> enumerator() {
@@ -108,17 +131,29 @@ public class Interpreter extends AbstractEnumerable<Object[]> {
   }
 
   /** Compiles an expression to an executable form. */
-  public Scalar compile(List<RexNode> nodes, List<RelNode> inputs) {
-    return scalarCompiler.compile(inputs, nodes);
+  public Scalar compile(List<RexNode> nodes, RelDataType inputRowType) {
+    if (inputRowType == null) {
+      inputRowType = dataContext.getTypeFactory().builder().build();
+    }
+    return scalarCompiler.compile(nodes, inputRowType);
+  }
+
+  RelDataType combinedRowType(List<RelNode> inputs) {
+    final RelDataTypeFactory.FieldInfoBuilder builder =
+        dataContext.getTypeFactory().builder();
+    for (RelNode input : inputs) {
+      builder.addAll(input.getRowType().getFieldList());
+    }
+    return builder.build();
   }
 
   /** Not used. */
   private class FooCompiler implements ScalarCompiler {
-    public Scalar compile(List<RelNode> inputs, List<RexNode> nodes) {
+    public Scalar compile(List<RexNode> nodes, RelDataType inputRowType) {
       final RexNode node = nodes.get(0);
       if (node instanceof RexCall) {
         final RexCall call = (RexCall) node;
-        final Scalar argScalar = compile(inputs, call.getOperands());
+        final Scalar argScalar = compile(call.getOperands(), inputRowType);
         return new Scalar() {
           final Object[] args = new Object[call.getOperands().size()];
 
@@ -394,7 +429,7 @@ public class Interpreter extends AbstractEnumerable<Object[]> {
   /** Converts a list of expressions to a scalar that can compute their
    * values. */
   interface ScalarCompiler {
-    Scalar compile(List<RelNode> inputs, List<RexNode> nodes);
+    Scalar compile(List<RexNode> nodes, RelDataType inputRowType);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java b/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java
index 491efa9..e4cf9e6 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/JaninoRexCompiler.java
@@ -30,9 +30,7 @@ import org.apache.calcite.linq4j.tree.Expressions;
 import org.apache.calcite.linq4j.tree.MemberDeclaration;
 import org.apache.calcite.linq4j.tree.ParameterExpression;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
-import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexProgram;
@@ -67,13 +65,7 @@ public class JaninoRexCompiler implements Interpreter.ScalarCompiler {
     this.rexBuilder = rexBuilder;
   }
 
-  public Scalar compile(List<RelNode> inputs, List<RexNode> nodes) {
-    final RelDataTypeFactory.FieldInfoBuilder fieldBuilder =
-        rexBuilder.getTypeFactory().builder();
-    for (RelNode input : inputs) {
-      fieldBuilder.addAll(input.getRowType().getFieldList());
-    }
-    final RelDataType inputRowType = fieldBuilder.build();
+  public Scalar compile(List<RexNode> nodes, RelDataType inputRowType) {
     final RexProgramBuilder programBuilder =
         new RexProgramBuilder(inputRowType, rexBuilder);
     for (RexNode node : nodes) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java b/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java
index 498c8a3..6e2ae15 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/JoinNode.java
@@ -40,7 +40,7 @@ public class JoinNode implements Node {
     this.rightSource = interpreter.source(rel, 1);
     this.sink = interpreter.sink(rel);
     this.condition = interpreter.compile(ImmutableList.of(rel.getCondition()),
-        rel.getInputs());
+        interpreter.combinedRowType(rel.getInputs()));
     this.rel = rel;
     this.context = interpreter.createContext();
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/Nodes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/Nodes.java b/core/src/main/java/org/apache/calcite/interpreter/Nodes.java
index bcb5f6f..19f397a 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/Nodes.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/Nodes.java
@@ -16,13 +16,7 @@
  */
 package org.apache.calcite.interpreter;
 
-import org.apache.calcite.plan.RelOptCluster;
-import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.RelOptUtil;
-import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
-import org.apache.calcite.rel.core.Calc;
 import org.apache.calcite.rel.core.Filter;
 import org.apache.calcite.rel.core.Join;
 import org.apache.calcite.rel.core.Project;
@@ -31,14 +25,7 @@ import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rel.core.Window;
-import org.apache.calcite.rel.rules.FilterTableRule;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexUtil;
-import org.apache.calcite.schema.FilterableTable;
-import org.apache.calcite.schema.ProjectableFilterableTable;
-import org.apache.calcite.util.ImmutableIntList;
-import org.apache.calcite.util.Pair;
-import org.apache.calcite.util.mapping.Mappings;
 
 import com.google.common.collect.ImmutableList;
 
@@ -56,77 +43,6 @@ public class Nodes {
       super(interpreter);
     }
 
-    public void rewrite(Project project) {
-      RelNode input = project.getInput();
-      final Mappings.TargetMapping mapping = project.getMapping();
-      if (mapping == null) {
-        return;
-      }
-      RexNode condition;
-      if (input instanceof Filter) {
-        final Filter filter = (Filter) input;
-        condition = filter.getCondition();
-        input = filter.getInput();
-      } else {
-        condition = project.getCluster().getRexBuilder().makeLiteral(true);
-      }
-      if (input instanceof TableScan) {
-        final TableScan scan = (TableScan) input;
-        final RelOptTable table = scan.getTable();
-        final ProjectableFilterableTable projectableFilterableTable =
-            table.unwrap(ProjectableFilterableTable.class);
-        if (projectableFilterableTable != null) {
-          final FilterTableRule.FilterSplit filterSplit =
-              FilterTableRule.FilterSplit.of(projectableFilterableTable,
-                  condition, interpreter.getDataContext());
-          if (filterSplit.rejectedFilters.isEmpty()) {
-            // Only push down projects & filters if there are no rejected
-            // filters. The rejected filter might need columns that are not
-            // projected. See CALCITE-445.
-            rel = new FilterScan(project.getCluster(), project.getTraitSet(),
-                table, filterSplit.acceptedFilters,
-                ImmutableIntList.copyOf(Mappings.asList(mapping.inverse())));
-            rel = RelOptUtil.createFilter(rel,
-                RexUtil.apply(mapping, filterSplit.rejectedFilters));
-          }
-        }
-      }
-    }
-
-    public void rewrite(Filter filter) {
-      if (filter.getInput() instanceof TableScan) {
-        final TableScan scan = (TableScan) filter.getInput();
-        final RelOptTable table = scan.getTable();
-        final ProjectableFilterableTable projectableFilterableTable =
-            table.unwrap(ProjectableFilterableTable.class);
-        if (projectableFilterableTable != null) {
-          final FilterTableRule.FilterSplit filterSplit =
-              FilterTableRule.FilterSplit.of(projectableFilterableTable,
-                  filter.getCondition(),
-                  interpreter.getDataContext());
-          if (!filterSplit.acceptedFilters.isEmpty()) {
-            rel = new FilterScan(scan.getCluster(), scan.getTraitSet(),
-                table, filterSplit.acceptedFilters, null);
-            rel = RelOptUtil.createFilter(rel, filterSplit.rejectedFilters);
-            return;
-          }
-        }
-        final FilterableTable filterableTable =
-            table.unwrap(FilterableTable.class);
-        if (filterableTable != null) {
-          final FilterTableRule.FilterSplit filterSplit =
-              FilterTableRule.FilterSplit.of(filterableTable,
-                  filter.getCondition(),
-                  interpreter.getDataContext());
-          if (!filterSplit.acceptedFilters.isEmpty()) {
-            rel = new FilterScan(scan.getCluster(), scan.getTraitSet(),
-                table, filterSplit.acceptedFilters, null);
-            rel = RelOptUtil.createFilter(rel, filterSplit.rejectedFilters);
-          }
-        }
-      }
-    }
-
     public void visit(Aggregate agg) {
       node = new AggregateNode(interpreter, agg);
     }
@@ -139,32 +55,18 @@ public class Nodes {
       node = new ProjectNode(interpreter, project);
     }
 
-    /** Per {@link #rewrite(RelNode)}, writes to {@link #rel}.
-     *
-     * <p>We don't handle {@link org.apache.calcite.rel.core.Calc} directly.
-     * Expand to a {@link org.apache.calcite.rel.core.Project}
-     * on {@link org.apache.calcite.rel.core.Filter} (or just a
-     * {@link org.apache.calcite.rel.core.Project}). */
-    public void rewrite(Calc calc) {
-      final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter =
-          calc.getProgram().split();
-      rel = calc.getInput();
-      rel = RelOptUtil.createFilter(rel, projectFilter.right);
-      rel = RelOptUtil.createProject(rel, projectFilter.left,
-          calc.getRowType().getFieldNames());
-    }
-
     public void visit(Values value) {
       node = new ValuesNode(interpreter, value);
     }
 
     public void visit(TableScan scan) {
-      node = new TableScanNode(interpreter, scan, ImmutableList.<RexNode>of(),
-          null);
+      node = TableScanNode.create(interpreter, scan,
+          ImmutableList.<RexNode>of(), null);
     }
 
-    public void visit(FilterScan scan) {
-      node = new TableScanNode(interpreter, scan, scan.filters, scan.projects);
+    public void visit(Bindables.BindableTableScan scan) {
+      node = TableScanNode.create(interpreter, scan, scan.filters,
+          scan.projects);
     }
 
     public void visit(Sort sort) {
@@ -183,21 +85,6 @@ public class Nodes {
       node = new WindowNode(interpreter, window);
     }
   }
-
-  /** Table scan that applies filters and optionally projects. Only used in an
-   * interpreter. */
-  public static class FilterScan extends TableScan {
-    private final ImmutableList<RexNode> filters;
-    private final ImmutableIntList projects;
-
-    protected FilterScan(RelOptCluster cluster, RelTraitSet traits,
-        RelOptTable table, ImmutableList<RexNode> filters,
-        ImmutableIntList projects) {
-      super(cluster, traits, table);
-      this.filters = filters;
-      this.projects = projects;
-    }
-  }
 }
 
 // End Nodes.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/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 1737b8b..576bf6f 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/ProjectNode.java
@@ -30,7 +30,8 @@ public class ProjectNode extends AbstractSingleNode<Project> {
   public ProjectNode(Interpreter interpreter, Project rel) {
     super(interpreter, rel);
     this.projectCount = rel.getProjects().size();
-    this.scalar = interpreter.compile(rel.getProjects(), rel.getInputs());
+    this.scalar = interpreter.compile(rel.getProjects(),
+        rel.getInput().getRowType());
     this.context = interpreter.createContext();
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java b/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java
index 6f2c90f..8f86bec 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/TableScanNode.java
@@ -21,9 +21,15 @@ import org.apache.calcite.linq4j.Enumerable;
 import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.linq4j.Queryable;
 import org.apache.calcite.linq4j.function.Function1;
+import org.apache.calcite.linq4j.function.Predicate1;
 import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.runtime.Enumerables;
 import org.apache.calcite.schema.FilterableTable;
 import org.apache.calcite.schema.ProjectableFilterableTable;
@@ -31,11 +37,15 @@ import org.apache.calcite.schema.QueryableTable;
 import org.apache.calcite.schema.ScannableTable;
 import org.apache.calcite.schema.SchemaPlus;
 import org.apache.calcite.schema.Schemas;
+import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Util;
+import org.apache.calcite.util.mapping.Mapping;
+import org.apache.calcite.util.mapping.Mappings;
 
-import com.google.common.base.Preconditions;
+import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
 import java.lang.reflect.Field;
@@ -43,29 +53,25 @@ import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 import java.util.List;
 
+import static org.apache.calcite.util.Static.RESOURCE;
+
 /**
  * Interpreter node that implements a
  * {@link org.apache.calcite.rel.core.TableScan}.
  */
 public class TableScanNode implements Node {
   private final Sink sink;
-  private final TableScan rel;
-  private final ImmutableList<RexNode> filters;
-  private final DataContext root;
-  private final int[] projects;
+  private final Enumerable<Row> enumerable;
 
-  TableScanNode(Interpreter interpreter, TableScan rel,
-      ImmutableList<RexNode> filters, ImmutableIntList projects) {
-    this.rel = rel;
-    this.filters = Preconditions.checkNotNull(filters);
-    this.projects = projects == null ? null : projects.toIntArray();
+  private TableScanNode(Interpreter interpreter, TableScan rel,
+      Enumerable<Row> enumerable) {
+    this.enumerable = enumerable;
     this.sink = interpreter.sink(rel);
-    this.root = interpreter.getDataContext();
   }
 
+
   public void run() throws InterruptedException {
-    final Enumerable<Row> iterable = iterable();
-    final Enumerator<Row> enumerator = iterable.enumerator();
+    final Enumerator<Row> enumerator = enumerable.enumerator();
     while (enumerator.moveNext()) {
       sink.send(enumerator.current());
     }
@@ -73,59 +79,108 @@ public class TableScanNode implements Node {
     sink.end();
   }
 
-  private Enumerable<Row> iterable() {
-    final RelOptTable table = rel.getTable();
+  /** Creates a TableScanNode.
+   *
+   * <p>Tries various table SPIs, and negotiates with the table which filters
+   * and projects it can implement. Adds to the Enumerable implementations of
+   * any filters and projects that cannot be implemented by the table. */
+  static TableScanNode create(Interpreter interpreter, TableScan rel,
+      ImmutableList<RexNode> filters, ImmutableIntList projects) {
+    final DataContext root = interpreter.getDataContext();
+    final RelOptTable relOptTable = rel.getTable();
     final ProjectableFilterableTable pfTable =
-        table.unwrap(ProjectableFilterableTable.class);
+        relOptTable.unwrap(ProjectableFilterableTable.class);
     if (pfTable != null) {
-      final List<RexNode> filters1 = Lists.newArrayList(filters);
-      final int[] projects1 =
-          projects == null
-              || isIdentity(projects, rel.getRowType().getFieldCount())
-              ? null : projects;
-      final Enumerable<Object[]> enumerator =
-          pfTable.scan(root, filters1, projects1);
-      assert filters1.isEmpty()
-          : "table could not handle a filter it earlier said it could";
-      return Enumerables.toRow(enumerator);
-    }
-    if (projects != null) {
-      throw new AssertionError("have projects, but table cannot handle them");
+      final ImmutableIntList originalProjects = projects;
+      for (;;) {
+        final List<RexNode> mutableFilters = Lists.newArrayList(filters);
+        final int[] projectInts;
+        if (projects == null
+            || projects.equals(TableScan.identity(relOptTable))) {
+          projectInts = null;
+        } else {
+          projectInts = projects.toIntArray();
+        }
+        final Enumerable<Object[]> enumerable1 =
+            pfTable.scan(root, mutableFilters, projectInts);
+        for (RexNode filter : mutableFilters) {
+          if (!filters.contains(filter)) {
+            throw RESOURCE.filterableTableInventedFilter(filter.toString())
+                .ex();
+          }
+        }
+        final ImmutableBitSet usedFields =
+            RelOptUtil.InputFinder.bits(mutableFilters, null);
+        if (projects != null) {
+          int changeCount = 0;
+          for (int usedField : usedFields) {
+            if (!projects.contains(usedField)) {
+              // A field that is not projected is used in a filter that the
+              // table rejected. We won't be able to apply the filter later.
+              // Try again without any projects.
+              projects =
+                  ImmutableIntList.copyOf(
+                      Iterables.concat(projects, ImmutableList.of(usedField)));
+              ++changeCount;
+            }
+          }
+          if (changeCount > 0) {
+            continue;
+          }
+        }
+        final Enumerable<Row> rowEnumerable = Enumerables.toRow(enumerable1);
+        final ImmutableIntList rejectedProjects;
+        if (Objects.equal(projects, originalProjects)) {
+          rejectedProjects = null;
+        } else {
+          // We projected extra columns because they were needed in filters. Now
+          // project the leading columns.
+          rejectedProjects = ImmutableIntList.identity(originalProjects.size());
+        }
+        return create2(interpreter, rel, rowEnumerable,
+            projects, mutableFilters, rejectedProjects);
+      }
     }
     final FilterableTable filterableTable =
-        table.unwrap(FilterableTable.class);
+        relOptTable.unwrap(FilterableTable.class);
     if (filterableTable != null) {
-      final List<RexNode> filters1 = Lists.newArrayList(filters);
-      final Enumerable<Object[]> enumerator =
-          filterableTable.scan(root, filters1);
-      assert filters1.isEmpty()
-          : "table could not handle a filter it earlier said it could";
-      return Enumerables.toRow(enumerator);
-    }
-    if (!filters.isEmpty()) {
-      throw new AssertionError("have filters, but table cannot handle them");
+      final List<RexNode> mutableFilters = Lists.newArrayList(filters);
+      final Enumerable<Object[]> enumerable =
+          filterableTable.scan(root, mutableFilters);
+      for (RexNode filter : mutableFilters) {
+        if (!filters.contains(filter)) {
+          throw RESOURCE.filterableTableInventedFilter(filter.toString()).ex();
+        }
+      }
+      final Enumerable<Row> rowEnumerable = Enumerables.toRow(enumerable);
+      return create2(interpreter, rel, rowEnumerable, null, mutableFilters,
+          projects);
     }
     final ScannableTable scannableTable =
-        table.unwrap(ScannableTable.class);
+        relOptTable.unwrap(ScannableTable.class);
     if (scannableTable != null) {
-      return Enumerables.toRow(scannableTable.scan(root));
+      final Enumerable<Row> rowEnumerable =
+          Enumerables.toRow(scannableTable.scan(root));
+      return create2(interpreter, rel, rowEnumerable, null, filters, projects);
     }
     //noinspection unchecked
-    Enumerable<Row> iterable = table.unwrap(Enumerable.class);
-    if (iterable != null) {
-      return iterable;
+    final Enumerable<Row> enumerable = relOptTable.unwrap(Enumerable.class);
+    if (enumerable != null) {
+      return create2(interpreter, rel, enumerable, null, filters, projects);
     }
-    final QueryableTable queryableTable = table.unwrap(QueryableTable.class);
+    final QueryableTable queryableTable =
+        relOptTable.unwrap(QueryableTable.class);
     if (queryableTable != null) {
       final Type elementType = queryableTable.getElementType();
       SchemaPlus schema = root.getRootSchema();
-      for (String name : Util.skipLast(table.getQualifiedName())) {
+      for (String name : Util.skipLast(relOptTable.getQualifiedName())) {
         schema = schema.getSubSchema(name);
       }
+      final Enumerable<Row> rowEnumerable;
       if (elementType instanceof Class) {
         //noinspection unchecked
         final Queryable<Object> queryable = Schemas.queryable(root,
-            (Class) elementType, table.getQualifiedName());
+            (Class) elementType, relOptTable.getQualifiedName());
         ImmutableList.Builder<Field> fieldBuilder = ImmutableList.builder();
         Class type = (Class) elementType;
         for (Field field : type.getFields()) {
@@ -135,7 +190,7 @@ public class TableScanNode implements Node {
           }
         }
         final List<Field> fields = fieldBuilder.build();
-        return queryable.select(
+        rowEnumerable = queryable.select(
             new Function1<Object, Row>() {
               public Row apply(Object o) {
                 final Object[] values = new Object[fields.size()];
@@ -151,23 +206,68 @@ public class TableScanNode implements Node {
               }
             });
       } else {
-        return Schemas.queryable(root, Row.class,
-            table.getQualifiedName());
+        rowEnumerable =
+            Schemas.queryable(root, Row.class, relOptTable.getQualifiedName());
       }
+      return create2(interpreter, rel, rowEnumerable, null, filters, projects);
     }
-    throw new AssertionError("cannot convert table " + table + " to iterable");
+    throw new AssertionError("cannot convert table " + relOptTable
+        + " to enumerable");
   }
 
-  private static boolean isIdentity(int[] is, int count) {
-    if (is.length != count) {
-      return false;
-    }
-    for (int i = 0; i < is.length; i++) {
-      if (is[i] != i) {
-        return false;
+  private static TableScanNode create2(Interpreter interpreter, TableScan rel,
+      Enumerable<Row> enumerable, final ImmutableIntList acceptedProjects,
+      List<RexNode> rejectedFilters, final ImmutableIntList rejectedProjects) {
+    if (!rejectedFilters.isEmpty()) {
+      final RexNode filter =
+          RexUtil.composeConjunction(rel.getCluster().getRexBuilder(),
+              rejectedFilters, false);
+      // Re-map filter for the projects that have been applied already
+      final RexNode filter2;
+      final RelDataType inputRowType;
+      if (acceptedProjects == null) {
+        filter2 = filter;
+        inputRowType = rel.getRowType();
+      } else {
+        final Mapping mapping = Mappings.target(acceptedProjects,
+            rel.getTable().getRowType().getFieldCount());
+        filter2 = RexUtil.apply(mapping, filter);
+        final RelDataTypeFactory.FieldInfoBuilder builder =
+            rel.getCluster().getTypeFactory().builder();
+        final List<RelDataTypeField> fieldList =
+            rel.getTable().getRowType().getFieldList();
+        for (int acceptedProject : acceptedProjects) {
+          builder.add(fieldList.get(acceptedProject));
+        }
+        inputRowType = builder.build();
       }
+      final Scalar condition =
+          interpreter.compile(
+              ImmutableList.of(filter2), inputRowType);
+      final Context context = interpreter.createContext();
+      enumerable = enumerable.where(
+          new Predicate1<Row>() {
+            @Override public boolean apply(Row row) {
+              context.values = row.getValues();
+              Boolean b = (Boolean) condition.execute(context);
+              return b != null && b;
+            }
+          });
+    }
+    if (rejectedProjects != null) {
+      enumerable = enumerable.select(
+          new Function1<Row, Row>() {
+            final Object[] values = new Object[rejectedProjects.size()];
+            @Override public Row apply(Row row) {
+              final Object[] inValues = row.getValues();
+              for (int i = 0; i < rejectedProjects.size(); i++) {
+                values[i] = inValues[rejectedProjects.get(i)];
+              }
+              return Row.asCopy(values);
+            }
+          });
     }
-    return true;
+    return new TableScanNode(interpreter, rel, enumerable);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java b/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java
index ff8c950..008d6f0 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/ValuesNode.java
@@ -16,7 +16,6 @@
  */
 package org.apache.calcite.interpreter;
 
-import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Values;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
@@ -47,8 +46,7 @@ public class ValuesNode implements Node {
     for (ImmutableList<RexLiteral> tuple : tuples) {
       nodes.addAll(tuple);
     }
-    final Scalar scalar =
-        interpreter.compile(nodes, ImmutableList.<RelNode>of());
+    final Scalar scalar = interpreter.compile(nodes, null);
     final Object[] values = new Object[nodes.size()];
     final Context context = interpreter.createContext();
     scalar.execute(context, values);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/model/ModelHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/model/ModelHandler.java b/core/src/main/java/org/apache/calcite/model/ModelHandler.java
index 20a4596..01ed89e 100644
--- a/core/src/main/java/org/apache/calcite/model/ModelHandler.java
+++ b/core/src/main/java/org/apache/calcite/model/ModelHandler.java
@@ -220,6 +220,9 @@ public class ModelHandler {
 
   /** Adds extra entries to an operand to a custom schema. */
   protected Map<String, Object> operandMap(Map<String, Object> operand) {
+    if (operand == null) {
+      return ImmutableMap.of();
+    }
     final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
     builder.putAll(operand);
     for (ExtraOperand extraOperand : ExtraOperand.values()) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/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 1b2e75f..2f884c1 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -20,6 +20,7 @@ import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.enumerable.EnumerableBindable;
 import org.apache.calcite.adapter.enumerable.EnumerableConvention;
 import org.apache.calcite.adapter.enumerable.EnumerableInterpretable;
+import org.apache.calcite.adapter.enumerable.EnumerableInterpreterRule;
 import org.apache.calcite.adapter.enumerable.EnumerableRel;
 import org.apache.calcite.adapter.enumerable.EnumerableRules;
 import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
@@ -70,13 +71,13 @@ import org.apache.calcite.rel.rules.AggregateStarTableRule;
 import org.apache.calcite.rel.rules.FilterAggregateTransposeRule;
 import org.apache.calcite.rel.rules.FilterJoinRule;
 import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
-import org.apache.calcite.rel.rules.FilterTableRule;
+import org.apache.calcite.rel.rules.FilterTableScanRule;
 import org.apache.calcite.rel.rules.JoinAssociateRule;
 import org.apache.calcite.rel.rules.JoinCommuteRule;
 import org.apache.calcite.rel.rules.JoinPushThroughJoinRule;
 import org.apache.calcite.rel.rules.ProjectFilterTransposeRule;
 import org.apache.calcite.rel.rules.ProjectMergeRule;
-import org.apache.calcite.rel.rules.ProjectTableRule;
+import org.apache.calcite.rel.rules.ProjectTableScanRule;
 import org.apache.calcite.rel.rules.ReduceExpressionsRule;
 import org.apache.calcite.rel.rules.SortProjectTransposeRule;
 import org.apache.calcite.rel.rules.TableScanRule;
@@ -181,6 +182,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
           EnumerableRules.ENUMERABLE_TABLE_MODIFICATION_RULE,
           EnumerableRules.ENUMERABLE_VALUES_RULE,
           EnumerableRules.ENUMERABLE_WINDOW_RULE,
+          EnumerableRules.ENUMERABLE_TABLE_SCAN_RULE,
           EnumerableRules.ENUMERABLE_TABLE_FUNCTION_SCAN_RULE);
 
   private static final List<RelOptRule> DEFAULT_RULES =
@@ -191,9 +193,7 @@ public class CalcitePrepareImpl implements CalcitePrepare {
           COMMUTE
               ? JoinAssociateRule.INSTANCE
               : ProjectMergeRule.INSTANCE,
-          FilterTableRule.INSTANCE,
-          ProjectTableRule.INSTANCE,
-          ProjectTableRule.INSTANCE2,
+          FilterTableScanRule.INSTANCE,
           ProjectFilterTransposeRule.INSTANCE,
           FilterProjectTransposeRule.INSTANCE,
           FilterJoinRule.FILTER_ON_JOIN,
@@ -322,11 +322,15 @@ public class CalcitePrepareImpl implements CalcitePrepare {
         planner.addRule(rule);
       }
     }
+    planner.addRule(Bindables.BINDABLE_TABLE_SCAN_RULE);
+    planner.addRule(ProjectTableScanRule.INSTANCE);
+    planner.addRule(ProjectTableScanRule.INTERPRETER);
 
     if (ENABLE_ENUMERABLE) {
       for (RelOptRule rule : ENUMERABLE_RULES) {
         planner.addRule(rule);
       }
+      planner.addRule(EnumerableInterpreterRule.INSTANCE);
     }
 
     if (ENABLE_BINDABLE && ENABLE_ENUMERABLE) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
index 735b344..e47ea5b 100644
--- a/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/RelOptTableImpl.java
@@ -16,15 +16,12 @@
  */
 package org.apache.calcite.prepare;
 
-import org.apache.calcite.adapter.enumerable.EnumerableConvention;
-import org.apache.calcite.adapter.enumerable.EnumerableInterpreter;
 import org.apache.calcite.adapter.enumerable.EnumerableTableScan;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.linq4j.tree.Expression;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptSchema;
 import org.apache.calcite.plan.RelOptTable;
-import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelDistribution;
 import org.apache.calcite.rel.RelDistributionTraitDef;
@@ -47,9 +44,9 @@ import org.apache.calcite.util.Util;
 
 import com.google.common.base.Function;
 import com.google.common.base.Functions;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
-import java.lang.reflect.Type;
 import java.util.List;
 
 /**
@@ -79,13 +76,11 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
       Function<Class, Expression> expressionFunction,
       Double rowCount) {
     this.schema = schema;
-    this.rowType = rowType;
+    this.rowType = Preconditions.checkNotNull(rowType);
     this.names = ImmutableList.copyOf(names);
     this.table = table; // may be null
-    this.expressionFunction = expressionFunction;
-    this.rowCount = rowCount;
-    assert expressionFunction != null;
-    assert rowType != null;
+    this.expressionFunction = expressionFunction; // may be null
+    this.rowCount = rowCount; // may be null
   }
 
   public static RelOptTableImpl create(
@@ -102,11 +97,18 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
 
   public static RelOptTableImpl create(RelOptSchema schema, RelDataType rowType,
       final CalciteSchema.TableEntry tableEntry, Double rowCount) {
-    Function<Class, Expression> expressionFunction;
     final Table table = tableEntry.getTable();
+    Function<Class, Expression> expressionFunction =
+        getClassExpressionFunction(tableEntry, table);
+    return new RelOptTableImpl(schema, rowType, tableEntry.path(),
+        table, expressionFunction, rowCount);
+  }
+
+  private static Function<Class, Expression> getClassExpressionFunction(
+      final CalciteSchema.TableEntry tableEntry, final Table table) {
     if (table instanceof QueryableTable) {
       final QueryableTable queryableTable = (QueryableTable) table;
-      expressionFunction = new Function<Class, Expression>() {
+      return new Function<Class, Expression>() {
         public Expression apply(Class clazz) {
           return queryableTable.getExpression(tableEntry.schema.plus(),
               tableEntry.name, clazz);
@@ -115,36 +117,31 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
     } else if (table instanceof ScannableTable
         || table instanceof FilterableTable
         || table instanceof ProjectableFilterableTable) {
-      expressionFunction = new Function<Class, Expression>() {
+      return new Function<Class, Expression>() {
         public Expression apply(Class clazz) {
           return Schemas.tableExpression(tableEntry.schema.plus(),
-              Object[].class, tableEntry.name,
+              Object[].class,
+              tableEntry.name,
               table.getClass());
         }
       };
     } else {
-      expressionFunction = new Function<Class, Expression>() {
+      return new Function<Class, Expression>() {
         public Expression apply(Class input) {
           throw new UnsupportedOperationException();
         }
       };
     }
-    return new RelOptTableImpl(schema, rowType, tableEntry.path(),
-        table, expressionFunction, rowCount);
   }
 
   public static RelOptTableImpl create(
       RelOptSchema schema,
       RelDataType rowType,
-      TranslatableTable table) {
-    final Function<Class, Expression> expressionFunction =
-        new Function<Class, Expression>() {
-          public Expression apply(Class input) {
-            throw new UnsupportedOperationException();
-          }
-        };
+      Table table) {
+    assert table instanceof TranslatableTable
+        || table instanceof ScannableTable;
     return new RelOptTableImpl(schema, rowType, ImmutableList.<String>of(),
-        table, expressionFunction, null);
+        table, null, null);
   }
 
   public <T> T unwrap(Class<T> clazz) {
@@ -163,6 +160,9 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
   }
 
   public Expression getExpression(Class clazz) {
+    if (expressionFunction == null) {
+      return null;
+    }
     return expressionFunction.apply(clazz);
   }
 
@@ -199,48 +199,23 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
     if (table instanceof TranslatableTable) {
       return ((TranslatableTable) table).toRel(context, this);
     }
+    final RelOptCluster cluster = context.getCluster();
     if (CalcitePrepareImpl.ENABLE_BINDABLE) {
-      return LogicalTableScan.create(context.getCluster(), this);
+      return LogicalTableScan.create(cluster, this);
     }
-    if (CalcitePrepareImpl.ENABLE_ENUMERABLE) {
-      RelOptCluster cluster = context.getCluster();
-      Class elementType = deduceElementType();
-      RelTraitSet traits = cluster.traitSetOf(EnumerableConvention.INSTANCE);
-      if (table != null) {
-        final List<RelCollation> collations =
-            table.getStatistic().getCollations();
-        if (!collations.isEmpty()) {
-          traits = traits.replace(collations);
-        }
-      }
-      final RelNode scan =
-          new EnumerableTableScan(cluster, traits, this, elementType);
-      if (table instanceof FilterableTable
-          || table instanceof ProjectableFilterableTable) {
-        return new EnumerableInterpreter(cluster, scan.getTraitSet(),
-            scan, 1d);
-      }
-      return scan;
+    if (CalcitePrepareImpl.ENABLE_ENUMERABLE
+        && table instanceof QueryableTable) {
+      return EnumerableTableScan.create(cluster, this);
     }
-    throw new AssertionError();
-  }
-
-  private Class deduceElementType() {
-    if (table instanceof QueryableTable) {
-      final QueryableTable queryableTable = (QueryableTable) table;
-      final Type type = queryableTable.getElementType();
-      if (type instanceof Class) {
-        return (Class) type;
-      } else {
-        return Object[].class;
-      }
-    } else if (table instanceof ScannableTable
+    if (table instanceof ScannableTable
         || table instanceof FilterableTable
         || table instanceof ProjectableFilterableTable) {
-      return Object[].class;
-    } else {
-      return Object.class;
+      return LogicalTableScan.create(cluster, this);
     }
+    if (CalcitePrepareImpl.ENABLE_ENUMERABLE) {
+      return EnumerableTableScan.create(cluster, this);
+    }
+    throw new AssertionError();
   }
 
   public List<RelCollation> getCollationList() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
index 496e39b..40bf0cf 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/TableScan.java
@@ -32,6 +32,7 @@ import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.ImmutableIntList;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -50,11 +51,9 @@ public abstract class TableScan extends AbstractRelNode {
 
   //~ Constructors -----------------------------------------------------------
 
-  protected TableScan(
-      RelOptCluster cluster,
-      RelTraitSet traits,
+  protected TableScan(RelOptCluster cluster, RelTraitSet traitSet,
       RelOptTable table) {
-    super(cluster, traits);
+    super(cluster, traitSet);
     this.table = table;
     if (table.getRelOptSchema() != null) {
       cluster.getPlanner().registerSchema(table.getRelOptSchema());
@@ -97,6 +96,16 @@ public abstract class TableScan extends AbstractRelNode {
     return table.getRowType();
   }
 
+  /** Returns an identity projection for the given table. */
+  public static ImmutableIntList identity(RelOptTable table) {
+    return ImmutableIntList.identity(table.getRowType().getFieldCount());
+  }
+
+  /** Returns an identity projection. */
+  public ImmutableIntList identity() {
+    return identity(table);
+  }
+
   @Override public RelWriter explainTerms(RelWriter pw) {
     return super.explainTerms(pw)
         .item("table", table.getQualifiedName());

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableScan.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableScan.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableScan.java
index 10b777c..788e5a2 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableScan.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalTableScan.java
@@ -20,9 +20,15 @@ import org.apache.calcite.plan.Convention;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptTable;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelInput;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.schema.Table;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 
@@ -83,11 +89,25 @@ public final class LogicalTableScan extends TableScan {
     return this;
   }
 
-  /** Creates a LogicalTableScan. */
+  /** Creates a LogicalTableScan.
+   *  @param cluster Cluster
+   * @param relOptTable Table
+   */
   public static LogicalTableScan create(RelOptCluster cluster,
-      RelOptTable table) {
-    final RelTraitSet traitSet = cluster.traitSetOf(Convention.NONE);
-    return new LogicalTableScan(cluster, traitSet, table);
+      final RelOptTable relOptTable) {
+    final Table table = relOptTable.unwrap(Table.class);
+    final RelTraitSet traitSet =
+        cluster.traitSetOf(Convention.NONE)
+            .replaceIfs(RelCollationTraitDef.INSTANCE,
+                new Supplier<List<RelCollation>>() {
+                  public List<RelCollation> get() {
+                    if (table != null) {
+                      return table.getStatistic().getCollations();
+                    }
+                    return ImmutableList.of();
+                  }
+                });
+    return new LogicalTableScan(cluster, traitSet, relOptTable);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
index 3d3b761..c1f4a02 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalWindow.java
@@ -52,6 +52,8 @@ import java.util.List;
 import java.util.Map;
 
 /**
+ * Sub-class of {@link org.apache.calcite.rel.core.Window}
+ * not targeted at any particular engine or calling convention.
  */
 public final class LogicalWindow extends Window {
   /**

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/bd0b6061/core/src/main/java/org/apache/calcite/rel/rules/CalcSplitRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/CalcSplitRule.java b/core/src/main/java/org/apache/calcite/rel/rules/CalcSplitRule.java
new file mode 100644
index 0000000..276b1c4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rules/CalcSplitRule.java
@@ -0,0 +1,59 @@
+/*
+ * 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.rules;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Planner rule that converts a {@link Calc}
+ * to a {@link org.apache.calcite.rel.core.Project}
+ * and {@link Filter}.
+ *
+ * <p>Not enabled by default, as it works against the usual flow, which is to
+ * convert {@code Project} and {@code Filter} to {@code Calc}. But useful for
+ * specific tasks, such as optimizing before calling an
+ * {@link org.apache.calcite.interpreter.Interpreter}.
+ */
+public class CalcSplitRule extends RelOptRule {
+  public static final CalcSplitRule INSTANCE = new CalcSplitRule();
+
+  private CalcSplitRule() {
+    super(operand(Calc.class, any()));
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Calc calc = call.rel(0);
+    final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter =
+        calc.getProgram().split();
+    final RelNode rel = calc.getInput();
+    final RelNode rel2 = RelOptUtil.createFilter(rel, projectFilter.right);
+    final RelNode rel3 = RelOptUtil.createProject(rel2, projectFilter.left,
+        calc.getRowType().getFieldNames());
+    call.transformTo(rel3);
+  }
+}
+
+// End CalcSplitRule.java


Mime
View raw message