calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jh...@apache.org
Subject [2/9] git commit: [CALCITE-436] Simpler SPI to query Table
Date Tue, 28 Oct 2014 17:50:50 GMT
[CALCITE-436] Simpler SPI to query Table


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

Branch: refs/heads/master
Commit: 263640ff6c409d6a13225a01b0042afbc5226c2b
Parents: e4fcf2a
Author: Julian Hyde <jhyde@apache.org>
Authored: Sat Oct 11 17:45:52 2014 -0700
Committer: Julian Hyde <jhyde@apache.org>
Committed: Fri Oct 24 09:25:49 2014 -0700

----------------------------------------------------------------------
 .../hydromatic/avatica/AvaticaConnection.java   |   1 -
 .../avatica/AvaticaPrepareResult.java           |   2 +
 .../net/hydromatic/optiq/BuiltinMethod.java     |   7 +
 .../net/hydromatic/optiq/FilterableTable.java   |  44 ++
 .../optiq/ProjectableFilterableTable.java       |  63 +++
 .../net/hydromatic/optiq/ScannableTable.java    |  31 ++
 .../main/java/net/hydromatic/optiq/Schemas.java |  49 ++-
 .../optiq/impl/interpreter/Interpreter.java     | 130 +++++-
 .../optiq/impl/interpreter/Nodes.java           | 116 ++++-
 .../optiq/impl/interpreter/ProjectNode.java     |   2 +-
 .../hydromatic/optiq/impl/interpreter/Row.java  |  29 +-
 .../optiq/impl/interpreter/ScanNode.java        |  61 ++-
 .../optiq/impl/interpreter/SortNode.java        |   7 +-
 .../net/hydromatic/optiq/jdbc/MetaImpl.java     |  23 +-
 .../optiq/jdbc/OptiqConnectionImpl.java         |  18 +-
 .../net/hydromatic/optiq/jdbc/OptiqPrepare.java |   7 +
 .../optiq/prepare/OptiqPrepareImpl.java         |  11 +-
 .../optiq/prepare/RelOptTableImpl.java          |  39 +-
 .../optiq/rules/java/EnumerableRel.java         |  33 ++
 .../rules/java/EnumerableRelImplementor.java    |  45 +-
 .../hydromatic/optiq/rules/java/JavaRules.java  | 123 +++++-
 .../hydromatic/optiq/runtime/Enumerables.java   |  30 +-
 .../optiq/runtime/EnumeratorCursor.java         |   2 +-
 .../net/hydromatic/optiq/tools/Programs.java    |   1 +
 .../metadata/RelMdPercentageOriginalRows.java   |   5 +
 .../eigenbase/rel/rules/FilterTableRule.java    | 168 +++++++
 .../eigenbase/rel/rules/ProjectTableRule.java   | 167 +++++++
 .../rel/rules/PushProjectPastFilterRule.java    |   2 +-
 .../rel/rules/PushProjectPastJoinRule.java      |   2 +-
 .../org/eigenbase/rel/rules/PushProjector.java  |  14 +-
 .../java/org/eigenbase/relopt/RelOptUtil.java   |   6 +-
 .../resource/EigenbaseNewResource.java          |   6 +
 .../main/java/org/eigenbase/rex/RexProgram.java |  18 +
 core/src/main/java/org/eigenbase/util/Bug.java  |  21 +-
 .../resource/EigenbaseResource.properties       |   2 +
 .../optiq/impl/generate/RangeTable.java         |  12 +-
 .../hydromatic/optiq/test/InterpreterTest.java  |  39 +-
 .../net/hydromatic/optiq/test/OptiqSuite.java   |   1 +
 .../optiq/test/ReflectiveSchemaTest.java        |   7 +-
 .../optiq/test/ScannableTableTest.java          | 433 +++++++++++++++++++
 40 files changed, 1635 insertions(+), 142 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/avatica/src/main/java/net/hydromatic/avatica/AvaticaConnection.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/net/hydromatic/avatica/AvaticaConnection.java b/avatica/src/main/java/net/hydromatic/avatica/AvaticaConnection.java
index 1814f09..411e250 100644
--- a/avatica/src/main/java/net/hydromatic/avatica/AvaticaConnection.java
+++ b/avatica/src/main/java/net/hydromatic/avatica/AvaticaConnection.java
@@ -434,7 +434,6 @@ public abstract class AvaticaConnection implements Connection {
       return statement.getParameterValues();
     }
   }
-
 }
 
 // End AvaticaConnection.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/avatica/src/main/java/net/hydromatic/avatica/AvaticaPrepareResult.java
----------------------------------------------------------------------
diff --git a/avatica/src/main/java/net/hydromatic/avatica/AvaticaPrepareResult.java b/avatica/src/main/java/net/hydromatic/avatica/AvaticaPrepareResult.java
index 1cb3538..9ce74b2 100644
--- a/avatica/src/main/java/net/hydromatic/avatica/AvaticaPrepareResult.java
+++ b/avatica/src/main/java/net/hydromatic/avatica/AvaticaPrepareResult.java
@@ -17,6 +17,7 @@
 package net.hydromatic.avatica;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Result of preparing a statement.
@@ -25,6 +26,7 @@ public interface AvaticaPrepareResult {
   List<ColumnMetaData> getColumnList();
   String getSql();
   List<AvaticaParameter> getParameterList();
+  Map<String, Object> getInternalParameters();
 }
 
 // End AvaticaPrepareResult.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/BuiltinMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/BuiltinMethod.java b/core/src/main/java/net/hydromatic/optiq/BuiltinMethod.java
index 37063cf..9ed9b8f 100644
--- a/core/src/main/java/net/hydromatic/optiq/BuiltinMethod.java
+++ b/core/src/main/java/net/hydromatic/optiq/BuiltinMethod.java
@@ -22,6 +22,7 @@ import net.hydromatic.linq4j.expressions.Primitive;
 import net.hydromatic.linq4j.expressions.Types;
 import net.hydromatic.linq4j.function.*;
 
+import net.hydromatic.optiq.impl.interpreter.Row;
 import net.hydromatic.optiq.impl.java.ReflectiveSchema;
 import net.hydromatic.optiq.impl.jdbc.JdbcSchema;
 import net.hydromatic.optiq.runtime.*;
@@ -56,16 +57,22 @@ public enum BuiltinMethod {
   SCHEMA_GET_SUB_SCHEMA(Schema.class, "getSubSchema", String.class),
   SCHEMA_GET_TABLE(Schema.class, "getTable", String.class),
   SCHEMA_PLUS_UNWRAP(SchemaPlus.class, "unwrap", Class.class),
+  SCHEMAS_ENUMERABLE(Schemas.class, "enumerable", ScannableTable.class,
+      DataContext.class),
+  SCHEMAS_ENUMERABLE2(Schemas.class, "enumerable", FilterableTable.class,
+      DataContext.class),
   SCHEMAS_QUERYABLE(Schemas.class, "queryable", DataContext.class,
       SchemaPlus.class, Class.class, String.class),
   REFLECTIVE_SCHEMA_GET_TARGET(ReflectiveSchema.class, "getTarget"),
   DATA_CONTEXT_GET(DataContext.class, "get", String.class),
   DATA_CONTEXT_GET_ROOT_SCHEMA(DataContext.class, "getRootSchema"),
   JDBC_SCHEMA_DATA_SOURCE(JdbcSchema.class, "getDataSource"),
+  ROW_VALUE(Row.class, "getObject", int.class),
   RESULT_SET_ENUMERABLE_OF(ResultSetEnumerable.class, "of", DataSource.class,
       String.class, Function1.class),
   JOIN(ExtendedEnumerable.class, "join", Enumerable.class, Function1.class,
       Function1.class, Function2.class),
+  SLICE0(Enumerables.class, "slice0", Enumerable.class),
   SEMI_JOIN(Enumerables.class, "semiJoin", Enumerable.class, Enumerable.class,
       Function1.class, Function1.class),
   SELECT(ExtendedEnumerable.class, "select", Function1.class),

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/FilterableTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/FilterableTable.java b/core/src/main/java/net/hydromatic/optiq/FilterableTable.java
new file mode 100644
index 0000000..7bc3a26
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/FilterableTable.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 net.hydromatic.optiq;
+
+import net.hydromatic.linq4j.Enumerable;
+
+import org.eigenbase.rex.RexNode;
+
+import java.util.List;
+
+/**
+ * Table that can be scanned, optionally applying supplied filter expressions,
+ * without creating an intermediate relational expression.
+ *
+ * @see ScannableTable
+ */
+public interface FilterableTable extends Table {
+  /** Returns an enumerator over the rows in this Table. Each row is represented
+   * as an array of its column values.
+   *
+   * <p>The list of filters is mutable.
+   * If the table can implement a particular filter, it should remove that
+   * filter from the list.
+   * If it cannot implement a filter, it should leave it in the list.
+   * Any filters remaining will be implemented by the consuming Calcite
+   * operator. */
+  Enumerable<Object[]> scan(DataContext root, List<RexNode> filters);
+}
+
+// End FilterableTable.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/ProjectableFilterableTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/ProjectableFilterableTable.java b/core/src/main/java/net/hydromatic/optiq/ProjectableFilterableTable.java
new file mode 100644
index 0000000..e713b53
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/ProjectableFilterableTable.java
@@ -0,0 +1,63 @@
+/*
+ * 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 net.hydromatic.optiq;
+
+import net.hydromatic.linq4j.Enumerable;
+
+import org.eigenbase.rex.RexNode;
+
+import java.util.List;
+
+/**
+ * Table that can be scanned, optionally applying supplied filter expressions,
+ * and projecting a given list of columns,
+ * without creating an intermediate relational expression.
+ *
+ * <p>If you wish to write a table that can apply projects but not filters,
+ * simply decline all filters.</p>
+ *
+ * @see net.hydromatic.optiq.ScannableTable
+ * @see net.hydromatic.optiq.FilterableTable
+ */
+public interface ProjectableFilterableTable extends Table {
+  /** Returns an enumerable over the rows in this Table.
+   *
+   * <p>Each row is represented as an array of its column values.
+   *
+   * <p>The list of filters is mutable.
+   * If the table can implement a particular filter, it should remove that
+   * filter from the list.
+   * If it cannot implement a filter, it should leave it in the list.
+   * Any filters remaining will be implemented by the consuming Calcite
+   * operator.
+   *
+   * <p>The projects are zero-based.</p>
+   *
+   * @param root Execution context
+   * @param filters Mutable list of filters. The method should remove from the
+   *                list any filters that it cannot apply.
+   * @param projects List of projects. Each is the 0-based ordinal of the column
+   *                 to project.
+   * @return Enumerable over all rows that match the accepted filters, returning
+   * for each row an array of column values, one value for each ordinal in
+   * {@code projects}.
+   */
+  Enumerable<Object[]> scan(DataContext root, List<RexNode> filters,
+      int[] projects);
+}
+
+// End ProjectableFilterableTable.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/ScannableTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/ScannableTable.java b/core/src/main/java/net/hydromatic/optiq/ScannableTable.java
new file mode 100644
index 0000000..af76df9
--- /dev/null
+++ b/core/src/main/java/net/hydromatic/optiq/ScannableTable.java
@@ -0,0 +1,31 @@
+/*
+ * 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 net.hydromatic.optiq;
+
+import net.hydromatic.linq4j.Enumerable;
+
+/**
+ * Table that can be scanned without creating an intermediate relational
+ * expression.
+ */
+public interface ScannableTable extends Table {
+  /** Returns an enumerator over the rows in this Table. Each row is represented
+   * as an array of its column values. */
+  Enumerable<Object[]> scan(DataContext root);
+}
+
+// End ScannableTable.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/Schemas.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/Schemas.java b/core/src/main/java/net/hydromatic/optiq/Schemas.java
index 09167c3..6a7a16e 100644
--- a/core/src/main/java/net/hydromatic/optiq/Schemas.java
+++ b/core/src/main/java/net/hydromatic/optiq/Schemas.java
@@ -16,6 +16,7 @@
  */
 package net.hydromatic.optiq;
 
+import net.hydromatic.linq4j.Enumerable;
 import net.hydromatic.linq4j.QueryProvider;
 import net.hydromatic.linq4j.Queryable;
 import net.hydromatic.linq4j.expressions.*;
@@ -30,8 +31,10 @@ import net.hydromatic.optiq.materialize.Lattice;
 import org.eigenbase.reltype.RelDataType;
 import org.eigenbase.reltype.RelDataTypeFactory;
 import org.eigenbase.reltype.RelProtoDataType;
+import org.eigenbase.rex.RexNode;
 import org.eigenbase.sql.type.SqlTypeUtil;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 
@@ -152,6 +155,18 @@ public final class Schemas {
           expression(schema),
           BuiltinMethod.SCHEMA_GET_TABLE.method,
           Expressions.constant(tableName));
+      if (ScannableTable.class.isAssignableFrom(clazz)) {
+        return Expressions.call(
+            BuiltinMethod.SCHEMAS_ENUMERABLE.method,
+            Expressions.convert_(expression, ScannableTable.class),
+            DataContext.ROOT);
+      }
+      if (FilterableTable.class.isAssignableFrom(clazz)) {
+        return Expressions.call(
+            BuiltinMethod.SCHEMAS_ENUMERABLE2.method,
+            Expressions.convert_(expression, FilterableTable.class),
+            DataContext.ROOT);
+      }
     } else {
       expression = Expressions.call(
           BuiltinMethod.SCHEMAS_QUERYABLE.method,
@@ -160,8 +175,7 @@ public final class Schemas {
           Expressions.constant(elementType),
           Expressions.constant(tableName));
     }
-    return Types.castIfNecessary(
-        clazz, expression);
+    return Types.castIfNecessary(clazz, expression);
   }
 
   public static DataContext createDataContext(Connection connection) {
@@ -196,6 +210,37 @@ public final class Schemas {
     return table.asQueryable(root.getQueryProvider(), schema, tableName);
   }
 
+  /** Returns an {@link net.hydromatic.linq4j.Enumerable} over the rows of
+   * a given table, representing each row as an object array. */
+  public static Enumerable<Object[]> enumerable(final ScannableTable table,
+      final DataContext root) {
+    return table.scan(root);
+  }
+
+  /** Returns an {@link net.hydromatic.linq4j.Enumerable} over the rows of
+   * a given table, not applying any filters, representing each row as an object
+   * array. */
+  public static Enumerable<Object[]> enumerable(final FilterableTable table,
+      final DataContext root) {
+    return table.scan(root, ImmutableList.<RexNode>of());
+  }
+
+  /** Returns an {@link net.hydromatic.linq4j.Enumerable} over object arrays,
+   * given a fully-qualified table name which leads to a
+   * {@link ScannableTable}. */
+  public static Table table(DataContext root, String... names) {
+    SchemaPlus schema = root.getRootSchema();
+    final List<String> nameList = Arrays.asList(names);
+    for (Iterator<? extends String> iterator = nameList.iterator();;) {
+      String name = iterator.next();
+      if (iterator.hasNext()) {
+        schema = schema.getSubSchema(name);
+      } else {
+        return schema.getTable(name);
+      }
+    }
+  }
+
   /** Parses and validates a SQL query. For use within Calcite only. */
   public static OptiqPrepare.ParseResult parse(
       final OptiqConnection connection, final OptiqSchema schema,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Interpreter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Interpreter.java b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Interpreter.java
index 12ac66b..7607319 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Interpreter.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Interpreter.java
@@ -20,6 +20,7 @@ import net.hydromatic.linq4j.AbstractEnumerable;
 import net.hydromatic.linq4j.Enumerator;
 
 import net.hydromatic.optiq.DataContext;
+import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
 
 import org.eigenbase.rel.*;
 import org.eigenbase.rex.*;
@@ -28,8 +29,10 @@ import org.eigenbase.util.ReflectiveVisitDispatcher;
 import org.eigenbase.util.ReflectiveVisitor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
+import java.math.BigDecimal;
 import java.util.*;
 
 /**
@@ -39,26 +42,26 @@ import java.util.*;
  * particular it holds working state while the data flow graph is being
  * assembled.</p>
  */
-public class Interpreter extends AbstractEnumerable<Row> {
+public class Interpreter extends AbstractEnumerable<Object[]> {
   final Map<RelNode, NodeInfo> nodes = Maps.newLinkedHashMap();
   private final DataContext dataContext;
   private final RelNode rootRel;
+  private final Map<RelNode, List<RelNode>> relInputs = Maps.newHashMap();
 
   public Interpreter(DataContext dataContext, RelNode rootRel) {
     this.dataContext = dataContext;
-    this.rootRel = rootRel;
     Compiler compiler = new Nodes.CoreCompiler(this);
-    compiler.visit(rootRel, 0, null);
+    this.rootRel = compiler.visitRoot(rootRel);
   }
 
-  public Enumerator<Row> enumerator() {
+  public Enumerator<Object[]> enumerator() {
     start();
     final ArrayDeque<Row> queue = nodes.get(rootRel).sink.list;
-    return new Enumerator<Row>() {
+    return new Enumerator<Object[]>() {
       Row row;
 
-      public Row current() {
-        return row;
+      public Object[] current() {
+        return row.getValues();
       }
 
       public boolean moveNext() {
@@ -108,19 +111,55 @@ public class Interpreter extends AbstractEnumerable<Row> {
       return new Scalar() {
         public Object execute(final Context context) {
           final List<Object> args;
-          final Comparable o0;
-          final Comparable o1;
+          Comparable o0;
+          Comparable o1;
           switch (call.getKind()) {
           case LESS_THAN:
-            args = lazyArgs(context);
-            o0 = (Comparable) args.get(0);
-            o1 = (Comparable) args.get(1);
-            return o0 == null || o1 == null ? null : o0.compareTo(o1) < 0;
+          case LESS_THAN_OR_EQUAL:
           case GREATER_THAN:
+          case GREATER_THAN_OR_EQUAL:
+          case EQUALS:
+          case NOT_EQUALS:
             args = lazyArgs(context);
             o0 = (Comparable) args.get(0);
+            if (o0 == null) {
+              return null;
+            }
             o1 = (Comparable) args.get(1);
-            return o0 == null || o1 == null ? null : o0.compareTo(o1) > 0;
+            if (o1 == null) {
+              return null;
+            }
+            if (o0 instanceof BigDecimal) {
+              if (o1 instanceof Double || o1 instanceof Float) {
+                o1 = new BigDecimal(((Number) o1).doubleValue());
+              } else {
+                o1 = new BigDecimal(((Number) o1).longValue());
+              }
+            }
+            if (o1 instanceof BigDecimal) {
+              if (o0 instanceof Double || o0 instanceof Float) {
+                o0 = new BigDecimal(((Number) o0).doubleValue());
+              } else {
+                o0 = new BigDecimal(((Number) o0).longValue());
+              }
+            }
+            final int c = o0.compareTo(o1);
+            switch (call.getKind()) {
+            case LESS_THAN:
+              return c < 0;
+            case LESS_THAN_OR_EQUAL:
+              return c <= 0;
+            case GREATER_THAN:
+              return c > 0;
+            case GREATER_THAN_OR_EQUAL:
+              return c >= 0;
+            case EQUALS:
+              return c == 0;
+            case NOT_EQUALS:
+              return c != 0;
+            default:
+              throw new AssertionError("unknown expression " + call);
+            }
           default:
             throw new AssertionError("unknown expression " + call);
           }
@@ -154,7 +193,7 @@ public class Interpreter extends AbstractEnumerable<Row> {
   }
 
   public Source source(RelNode rel, int ordinal) {
-    final RelNode input = rel.getInput(ordinal);
+    final RelNode input = getInput(rel, ordinal);
     final NodeInfo x = nodes.get(input);
     if (x == null) {
       throw new AssertionError("should be registered: " + rel);
@@ -162,6 +201,14 @@ public class Interpreter extends AbstractEnumerable<Row> {
     return new ListSource(x.sink);
   }
 
+  private RelNode getInput(RelNode rel, int ordinal) {
+    final List<RelNode> inputs = relInputs.get(rel);
+    if (inputs != null) {
+      return inputs.get(ordinal);
+    }
+    return rel.getInput(ordinal);
+  }
+
   public Sink sink(RelNode rel) {
     final ArrayDeque<Row> queue = new ArrayDeque<Row>(1);
     final ListSink sink = new ListSink(queue);
@@ -240,17 +287,60 @@ public class Interpreter extends AbstractEnumerable<Row> {
     private final ReflectiveVisitDispatcher<Compiler, RelNode> dispatcher =
         ReflectUtil.createDispatcher(Compiler.class, RelNode.class);
     protected final Interpreter interpreter;
+    protected RelNode rootRel;
+    protected RelNode rel;
     protected Node node;
 
+    private static final String REWRITE_METHOD_NAME = "rewrite";
     private static final String VISIT_METHOD_NAME = "visit";
 
     Compiler(Interpreter interpreter) {
       this.interpreter = interpreter;
     }
 
+    public RelNode visitRoot(RelNode p) {
+      rootRel = p;
+      visit(p, 0, null);
+      return rootRel;
+    }
+
     @Override public void visit(RelNode p, int ordinal, RelNode parent) {
+      for (;;) {
+        rel = null;
+        boolean found = dispatcher.invokeVisitor(this, p, REWRITE_METHOD_NAME);
+        if (!found) {
+          throw new AssertionError(
+              "interpreter: no implementation for rewrite");
+        }
+        if (rel == null) {
+          break;
+        }
+        if (OptiqPrepareImpl.DEBUG) {
+          System.out.println("Interpreter: rewrite " + p + " to " + rel);
+        }
+        p = rel;
+        if (parent != null) {
+          List<RelNode> inputs = interpreter.relInputs.get(parent);
+          if (inputs == null) {
+            inputs = Lists.newArrayList(parent.getInputs());
+            interpreter.relInputs.put(parent, inputs);
+          }
+          inputs.set(ordinal, p);
+        } else {
+          rootRel = p;
+        }
+      }
+
       // rewrite children first (from left to right)
-      super.visit(p, ordinal, parent);
+      final List<RelNode> inputs = interpreter.relInputs.get(p);
+      if (inputs != null) {
+        for (int i = 0; i < inputs.size(); i++) {
+          RelNode input = inputs.get(i);
+          visit(input, i, p);
+        }
+      } else {
+        p.childrenAccept(this);
+      }
 
       node = null;
       boolean found = dispatcher.invokeVisitor(this, p, VISIT_METHOD_NAME);
@@ -263,6 +353,14 @@ public class Interpreter extends AbstractEnumerable<Row> {
       assert nodeInfo != null;
       nodeInfo.node = node;
     }
+
+    /** Fallback rewrite method.
+     *
+     * <p>Overriding methods (each with a different sub-class of {@link RelNode}
+     * as its argument type) sets the {@link #rel} field if intends to
+     * rewrite. */
+    public void rewrite(RelNode r) {
+    }
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Nodes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Nodes.java b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Nodes.java
index c2de888..7c5b43b 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Nodes.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Nodes.java
@@ -16,7 +16,21 @@
  */
 package net.hydromatic.optiq.impl.interpreter;
 
+import net.hydromatic.optiq.FilterableTable;
+import net.hydromatic.optiq.ProjectableFilterableTable;
+
 import org.eigenbase.rel.*;
+import org.eigenbase.rel.rules.FilterTableRule;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelOptUtil;
+import org.eigenbase.relopt.RelTraitSet;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.util.ImmutableIntList;
+import org.eigenbase.util.Pair;
+import org.eigenbase.util.mapping.Mappings;
+
+import com.google.common.collect.ImmutableList;
 
 /**
  * Helper methods for {@link Node} and implementations for core relational
@@ -32,6 +46,71 @@ public class Nodes {
       super(interpreter);
     }
 
+    public void rewrite(ProjectRelBase project) {
+      RelNode input = project.getChild();
+      final Mappings.TargetMapping mapping = project.getMapping();
+      if (mapping == null) {
+        return;
+      }
+      RexNode condition;
+      if (input instanceof FilterRelBase) {
+        final FilterRelBase filter = (FilterRelBase) input;
+        condition = filter.getCondition();
+        input = filter.getChild();
+      } else {
+        condition = project.getCluster().getRexBuilder().makeLiteral(true);
+      }
+      if (input instanceof TableAccessRelBase) {
+        final TableAccessRelBase scan = (TableAccessRelBase) 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());
+          rel = new FilterScanRel(project.getCluster(), project.getTraitSet(),
+              table, filterSplit.acceptedFilters,
+              ImmutableIntList.copyOf(Mappings.asList(mapping.inverse())));
+          rel = RelOptUtil.createFilter(rel, filterSplit.rejectedFilters);
+        }
+      }
+    }
+
+    public void rewrite(FilterRelBase filter) {
+      if (filter.getChild() instanceof TableAccessRelBase) {
+        final TableAccessRelBase scan = (TableAccessRelBase) filter.getChild();
+        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 FilterScanRel(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 FilterScanRel(scan.getCluster(), scan.getTraitSet(),
+                table, filterSplit.acceptedFilters, null);
+            rel = RelOptUtil.createFilter(rel, filterSplit.rejectedFilters);
+          }
+        }
+      }
+    }
+
     public void visit(FilterRelBase filter) {
       node = new FilterNode(interpreter, filter);
     }
@@ -40,18 +119,51 @@ public class Nodes {
       node = new ProjectNode(interpreter, project);
     }
 
+    /** Per {@link #rewrite(RelNode)}, writes to {@link #rel}.
+     *
+     * <p>We don't handle {@link CalcRelBase} directly. Expand to a
+     * {@link ProjectRelBase} on {@link FilterRelBase} (or just a
+     * {@link ProjectRelBase}). */
+    public void rewrite(CalcRelBase calc) {
+      final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter =
+          calc.getProgram().split();
+      rel = calc.getChild();
+      rel = RelOptUtil.createFilter(rel, projectFilter.right);
+      rel = RelOptUtil.createProject(rel, projectFilter.left,
+          calc.getRowType().getFieldNames());
+    }
+
     public void visit(ValuesRelBase value) {
       node = new ValuesNode(interpreter, value);
     }
 
     public void visit(TableAccessRelBase scan) {
-      node = new ScanNode(interpreter, scan);
+      node = new ScanNode(interpreter, scan, ImmutableList.<RexNode>of(), null);
+    }
+
+    public void visit(FilterScanRel scan) {
+      node = new ScanNode(interpreter, scan, scan.filters, scan.projects);
     }
 
     public void visit(SortRel sort) {
       node = new SortNode(interpreter, sort);
     }
   }
+
+  /** Table scan that applies filters and optionally projects. Only used in an
+   * interpreter. */
+  public static class FilterScanRel extends TableAccessRelBase {
+    private final ImmutableList<RexNode> filters;
+    private final ImmutableIntList projects;
+
+    protected FilterScanRel(RelOptCluster cluster, RelTraitSet traits,
+        RelOptTable table, ImmutableList<RexNode> filters,
+        ImmutableIntList projects) {
+      super(cluster, traits, table);
+      this.filters = filters;
+      this.projects = projects;
+    }
+  }
 }
 
-// End Node.java
+// End Nodes.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ProjectNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ProjectNode.java b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ProjectNode.java
index 7193057..1a2d597 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ProjectNode.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ProjectNode.java
@@ -55,4 +55,4 @@ public class ProjectNode implements Node {
   }
 }
 
-// End FilterNode.java
+// End ProjectNode.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Row.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Row.java b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Row.java
index e410704..c84acc1 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Row.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/Row.java
@@ -24,10 +24,37 @@ import java.util.Arrays;
 public class Row {
   private final Object[] values;
 
-  public Row(Object[] values) {
+  /** Creates a Row. */
+  // must stay package-protected, because does not copy
+  Row(Object[] values) {
     this.values = values;
   }
 
+  /** Creates a Row.
+   *
+   * <p>Makes a defensive copy of the array, so the Row is immutable.
+   * (If you're worried about the extra copy, call {@link #of(Object)}.
+   * But the JIT probably avoids the copy.)
+   */
+  public static Row asCopy(Object... values) {
+    return new Row(values.clone());
+  }
+
+  /** Creates a Row with one column value. */
+  public static Row of(Object value0) {
+    return new Row(new Object[] {value0});
+  }
+
+  /** Creates a Row with two column values. */
+  public static Row of(Object value0, Object value1) {
+    return new Row(new Object[] {value0, value1});
+  }
+
+  /** Creates a Row with three column values. */
+  public static Row of(Object value0, Object value1, Object value2) {
+    return new Row(new Object[] {value0, value1, value2});
+  }
+
   @Override public int hashCode() {
     return Arrays.hashCode(values);
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ScanNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ScanNode.java b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ScanNode.java
index 085ffce..778b473 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ScanNode.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/ScanNode.java
@@ -22,14 +22,23 @@ import net.hydromatic.linq4j.Queryable;
 import net.hydromatic.linq4j.function.Function1;
 
 import net.hydromatic.optiq.DataContext;
+import net.hydromatic.optiq.FilterableTable;
+import net.hydromatic.optiq.ProjectableFilterableTable;
 import net.hydromatic.optiq.QueryableTable;
+import net.hydromatic.optiq.ScannableTable;
 import net.hydromatic.optiq.SchemaPlus;
 import net.hydromatic.optiq.Schemas;
+import net.hydromatic.optiq.runtime.Enumerables;
 
 import org.eigenbase.rel.TableAccessRelBase;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.util.ImmutableIntList;
 import org.eigenbase.util.Util;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -43,10 +52,15 @@ import java.util.List;
 public class ScanNode implements Node {
   private final Sink sink;
   private final TableAccessRelBase rel;
+  private final ImmutableList<RexNode> filters;
   private final DataContext root;
+  private final int[] projects;
 
-  public ScanNode(Interpreter interpreter, TableAccessRelBase rel) {
+  public ScanNode(Interpreter interpreter, TableAccessRelBase rel,
+      ImmutableList<RexNode> filters, ImmutableIntList projects) {
     this.rel = rel;
+    this.filters = Preconditions.checkNotNull(filters);
+    this.projects = projects == null ? null : projects.toIntArray();
     this.sink = interpreter.sink(rel);
     this.root = interpreter.getDataContext();
   }
@@ -62,23 +76,49 @@ public class ScanNode implements Node {
   }
 
   private Enumerable<Row> iterable() {
+    final RelOptTable table = rel.getTable();
+    final ProjectableFilterableTable pfTable =
+        table.unwrap(ProjectableFilterableTable.class);
+    if (pfTable != null) {
+      final List<RexNode> filters1 = Lists.newArrayList(filters);
+      final Enumerable<Object[]> enumerator =
+          pfTable.scan(root, filters1, projects);
+      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 FilterableTable filterableTable =
+        table.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");
+    }
     //noinspection unchecked
-    Enumerable<Row> iterable = rel.getTable().unwrap(Enumerable.class);
+    Enumerable<Row> iterable = table.unwrap(Enumerable.class);
     if (iterable != null) {
       return iterable;
     }
-    final QueryableTable queryableTable =
-        rel.getTable().unwrap(QueryableTable.class);
+    final QueryableTable queryableTable = table.unwrap(QueryableTable.class);
     if (queryableTable != null) {
       final Type elementType = queryableTable.getElementType();
       SchemaPlus schema = root.getRootSchema();
-      for (String name : Util.skipLast(rel.getTable().getQualifiedName())) {
+      for (String name : Util.skipLast(table.getQualifiedName())) {
         schema = schema.getSubSchema(name);
       }
       if (elementType instanceof Class) {
         //noinspection unchecked
         final Queryable<Object> queryable = Schemas.queryable(root,
-            (Class) elementType, rel.getTable().getQualifiedName());
+            (Class) elementType, table.getQualifiedName());
         ImmutableList.Builder<Field> fieldBuilder = ImmutableList.builder();
         Class type = (Class) elementType;
         for (Field field : type.getFields()) {
@@ -105,10 +145,15 @@ public class ScanNode implements Node {
             });
       } else {
         return Schemas.queryable(root, Row.class,
-            rel.getTable().getQualifiedName());
+            table.getQualifiedName());
       }
     }
-    throw new AssertionError("cannot convert table " + rel.getTable()
+    final ScannableTable scannableTable =
+        table.unwrap(ScannableTable.class);
+    if (scannableTable != null) {
+      return Enumerables.toRow(scannableTable.scan(root));
+    }
+    throw new AssertionError("cannot convert table " + table
         + " to iterable");
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/impl/interpreter/SortNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/SortNode.java b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/SortNode.java
index 1f324c3..7f0c8a6 100644
--- a/core/src/main/java/net/hydromatic/optiq/impl/interpreter/SortNode.java
+++ b/core/src/main/java/net/hydromatic/optiq/impl/interpreter/SortNode.java
@@ -25,6 +25,7 @@ import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Ordering;
 
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -48,11 +49,11 @@ public class SortNode implements Node {
     final int offset =
         rel.offset == null
             ? 0
-            : (Integer) ((RexLiteral) rel.offset).getValue();
+            : ((BigDecimal) ((RexLiteral) rel.offset).getValue()).intValue();
     final int fetch =
         rel.fetch == null
             ? -1
-            : (Integer) ((RexLiteral) rel.fetch).getValue();
+            : ((BigDecimal) ((RexLiteral) rel.fetch).getValue()).intValue();
     // In pure limit mode. No sort required.
     Row row;
   loop:
@@ -64,7 +65,7 @@ public class SortNode implements Node {
         }
       }
       if (fetch >= 0) {
-        for (int i = 0; i < offset && (row = source.receive()) != null; i++) {
+        for (int i = 0; i < fetch && (row = source.receive()) != null; i++) {
           sink.send(row);
         }
       } else {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/jdbc/MetaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/MetaImpl.java b/core/src/main/java/net/hydromatic/optiq/jdbc/MetaImpl.java
index 887d1ea..ce807b7 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/MetaImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/MetaImpl.java
@@ -171,20 +171,20 @@ public class MetaImpl implements Meta {
   public static <E> ResultSet createEmptyResultSet(
       OptiqConnectionImpl connection,
       final Class<E> clazz) {
-    return createResultSet(
-        connection,
+    return createResultSet(connection, ImmutableMap.<String, Object>of(),
         fieldMetaData(clazz),
         new RecordEnumeratorCursor<E>(Linq4j.<E>emptyEnumerator(), clazz));
   }
 
   private static <E> ResultSet createResultSet(OptiqConnectionImpl connection,
+      final Map<String, Object> internalParameters,
       final ColumnMetaData.StructType structType,
       final Cursor cursor) {
     try {
       final AvaticaResultSet resultSet = connection.getFactory().newResultSet(
           connection.createStatement(),
           new OptiqPrepare.PrepareResult<E>("",
-              ImmutableList.<AvaticaParameter>of(), null,
+              ImmutableList.<AvaticaParameter>of(), internalParameters, null,
               structType, -1, null, Object.class) {
             @Override
             public Cursor createCursor(DataContext dataContext) {
@@ -203,7 +203,8 @@ public class MetaImpl implements Meta {
       final Enumerable<?> enumerable,
       final NamedFieldGetter columnGetter) {
     //noinspection unchecked
-    return createResultSet(connection, columnGetter.structType,
+    return createResultSet(connection, ImmutableMap.<String, Object>of(),
+        columnGetter.structType,
         columnGetter.cursor(((Enumerable) enumerable).enumerator()));
   }
 
@@ -603,10 +604,14 @@ public class MetaImpl implements Meta {
 
   public Cursor createCursor(AvaticaResultSet resultSet_) {
     OptiqResultSet resultSet = (OptiqResultSet) resultSet_;
-    final DataContext dataContext =
-        connection.createDataContext(
-            OptiqConnectionImpl.TROJAN.getParameterValues(
-                resultSet.getStatement()));
+    Map<String, Object> map = Maps.newLinkedHashMap();
+    final List<Object> parameterValues =
+        OptiqConnectionImpl.TROJAN.getParameterValues(resultSet.getStatement());
+    for (Ord<Object> o : Ord.zip(parameterValues)) {
+      map.put("?" + o.i, o.e);
+    }
+    map.putAll(resultSet.getPrepareResult().getInternalParameters());
+    final DataContext dataContext = connection.createDataContext(map);
     OptiqPrepare.PrepareResult prepareResult = resultSet.getPrepareResult();
     return prepareResult.createCursor(dataContext);
   }
@@ -623,7 +628,7 @@ public class MetaImpl implements Meta {
   @VisibleForTesting
   public static DataContext createDataContext(OptiqConnection connection) {
     return ((OptiqConnectionImpl) connection)
-        .createDataContext(ImmutableList.of());
+        .createDataContext(ImmutableMap.<String, Object>of());
   }
 
   /** A trojan-horse method, subject to change without notice. */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
index a1c8f57..34ddd64 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqConnectionImpl.java
@@ -213,14 +213,14 @@ abstract class OptiqConnectionImpl
       OptiqPrepare.PrepareResult<T> enumerable =
           statement.prepare(queryable);
       final DataContext dataContext =
-          createDataContext(Collections.emptyList());
+          createDataContext(ImmutableMap.<String, Object>of());
       return enumerable.enumerator(dataContext);
     } catch (SQLException e) {
       throw new RuntimeException(e);
     }
   }
 
-  public DataContext createDataContext(List<Object> parameterValues) {
+  public DataContext createDataContext(Map<String, Object> parameterValues) {
     if (config().spark()) {
       return new SlimDataContext();
     }
@@ -286,7 +286,7 @@ abstract class OptiqConnectionImpl
     private final JavaTypeFactory typeFactory;
 
     DataContextImpl(OptiqConnectionImpl connection,
-        List<Object> parameterValues) {
+        Map<String, Object> parameters) {
       this.queryProvider = connection;
       this.typeFactory = connection.getTypeFactory();
       this.rootSchema = connection.rootSchema;
@@ -308,12 +308,12 @@ abstract class OptiqConnectionImpl
           .put(Variable.CURRENT_TIMESTAMP.camelName, time + currentOffset)
           .put(Variable.LOCAL_TIMESTAMP.camelName, time + localOffset)
           .put(Variable.TIME_ZONE.camelName, timeZone);
-      for (Ord<Object> value : Ord.zip(parameterValues)) {
-        Object e = value.e;
+      for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+        Object e = entry.getValue();
         if (e == null) {
           e = AvaticaParameter.DUMMY_VALUE;
         }
-        builder.put("?" + value.i, e);
+        builder.put(entry.getKey(), e);
       }
       map = builder.build();
     }
@@ -376,8 +376,8 @@ abstract class OptiqConnectionImpl
     public List<String> getDefaultSchemaPath() {
       final String schemaName = connection.getSchema();
       return schemaName == null
-          ? Collections.<String>emptyList()
-          : Collections.singletonList(schemaName);
+          ? ImmutableList.<String>of()
+          : ImmutableList.of(schemaName);
     }
 
     public OptiqConnectionConfig config() {
@@ -385,7 +385,7 @@ abstract class OptiqConnectionImpl
     }
 
     public DataContext getDataContext() {
-      return connection.createDataContext(ImmutableList.of());
+      return connection.createDataContext(ImmutableMap.<String, Object>of());
     }
 
     public OptiqPrepare.SparkHandler spark() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
index f9f062c..2814546 100644
--- a/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
+++ b/core/src/main/java/net/hydromatic/optiq/jdbc/OptiqPrepare.java
@@ -221,6 +221,7 @@ public interface OptiqPrepare {
   public static class PrepareResult<T> implements AvaticaPrepareResult {
     public final String sql; // for debug
     public final List<AvaticaParameter> parameterList;
+    private final Map<String, Object> internalParameters;
     public final RelDataType rowType;
     public final ColumnMetaData.StructType structType;
     private final int maxRowCount;
@@ -229,6 +230,7 @@ public interface OptiqPrepare {
 
     public PrepareResult(String sql,
         List<AvaticaParameter> parameterList,
+        Map<String, Object> internalParameters,
         RelDataType rowType,
         ColumnMetaData.StructType structType,
         int maxRowCount,
@@ -237,6 +239,7 @@ public interface OptiqPrepare {
       super();
       this.sql = sql;
       this.parameterList = parameterList;
+      this.internalParameters = internalParameters;
       this.rowType = rowType;
       this.structType = structType;
       this.maxRowCount = maxRowCount;
@@ -262,6 +265,10 @@ public interface OptiqPrepare {
       return parameterList;
     }
 
+    public Map<String, Object> getInternalParameters() {
+      return internalParameters;
+    }
+
     public String getSql() {
       return sql;
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
index 1b8cd9e..130638e 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/OptiqPrepareImpl.java
@@ -128,6 +128,10 @@ public class OptiqPrepareImpl implements OptiqPrepare {
           COMMUTE
               ? CommutativeJoinRule.INSTANCE
               : MergeProjectRule.INSTANCE,
+          FilterTableRule.INSTANCE,
+          ProjectTableRule.INSTANCE,
+          ProjectTableRule.INSTANCE2,
+          PushProjectPastFilterRule.INSTANCE,
           PushFilterPastProjectRule.INSTANCE,
           PushFilterPastJoinRule.FILTER_ON_JOIN,
           RemoveDistinctAggregateRule.INSTANCE,
@@ -339,6 +343,7 @@ public class OptiqPrepareImpl implements OptiqPrepare {
     return new PrepareResult<T>(
         sql,
         ImmutableList.<AvaticaParameter>of(),
+        ImmutableMap.<String, Object>of(),
         x,
         getColumnMetaDataList(typeFactory, x, x, origins),
         -1,
@@ -453,6 +458,7 @@ public class OptiqPrepareImpl implements OptiqPrepare {
     return new PrepareResult<T>(
         sql,
         parameters,
+        preparingStmt.internalParameters,
         jdbcType,
         structType,
         maxRowCount,
@@ -622,7 +628,8 @@ public class OptiqPrepareImpl implements OptiqPrepare {
     protected final OptiqSchema schema;
     protected final RelDataTypeFactory typeFactory;
     private final EnumerableRel.Prefer prefer;
-
+    private final Map<String, Object> internalParameters =
+        Maps.newLinkedHashMap();
     private int expansionDepth;
     private SqlValidator sqlValidator;
 
@@ -706,7 +713,7 @@ public class OptiqPrepareImpl implements OptiqPrepare {
     @Override
     protected EnumerableRelImplementor getRelImplementor(
         RexBuilder rexBuilder) {
-      return new EnumerableRelImplementor(rexBuilder);
+      return new EnumerableRelImplementor(rexBuilder, internalParameters);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java b/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
index d5f47aa..21b3dbf 100644
--- a/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
+++ b/core/src/main/java/net/hydromatic/optiq/prepare/RelOptTableImpl.java
@@ -18,10 +18,7 @@ package net.hydromatic.optiq.prepare;
 
 import net.hydromatic.linq4j.expressions.Expression;
 
-import net.hydromatic.optiq.QueryableTable;
-import net.hydromatic.optiq.Schemas;
-import net.hydromatic.optiq.Table;
-import net.hydromatic.optiq.TranslatableTable;
+import net.hydromatic.optiq.*;
 import net.hydromatic.optiq.jdbc.OptiqSchema;
 import net.hydromatic.optiq.rules.java.EnumerableConvention;
 import net.hydromatic.optiq.rules.java.JavaRules;
@@ -95,14 +92,25 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
   public static RelOptTableImpl create(RelOptSchema schema, RelDataType rowType,
       final OptiqSchema.TableEntry tableEntry, Double rowCount) {
     Function<Class, Expression> expressionFunction;
-    if (tableEntry.getTable() instanceof QueryableTable) {
-      final QueryableTable table = (QueryableTable) tableEntry.getTable();
+    final Table table = tableEntry.getTable();
+    if (table instanceof QueryableTable) {
+      final QueryableTable queryableTable = (QueryableTable) table;
       expressionFunction = new Function<Class, Expression>() {
         public Expression apply(Class clazz) {
-          return table.getExpression(tableEntry.schema.plus(),
+          return queryableTable.getExpression(tableEntry.schema.plus(),
               tableEntry.name, clazz);
         }
       };
+    } else if (table instanceof ScannableTable
+        || table instanceof FilterableTable
+        || table instanceof ProjectableFilterableTable) {
+      expressionFunction = new Function<Class, Expression>() {
+        public Expression apply(Class clazz) {
+          return Schemas.tableExpression(tableEntry.schema.plus(),
+              Object[].class, tableEntry.name,
+              table.getClass());
+        }
+      };
     } else {
       expressionFunction = new Function<Class, Expression>() {
         public Expression apply(Class input) {
@@ -111,7 +119,7 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
       };
     }
     return new RelOptTableImpl(schema, rowType, tableEntry.path(),
-      tableEntry.getTable(), expressionFunction, rowCount);
+        table, expressionFunction, rowCount);
   }
 
   public static RelOptTableImpl create(
@@ -170,9 +178,14 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
     }
     RelOptCluster cluster = context.getCluster();
     Class elementType = deduceElementType();
-    return new JavaRules.EnumerableTableAccessRel(
-        cluster, cluster.traitSetOf(EnumerableConvention.INSTANCE),
-        this, elementType);
+    final RelNode scan = new JavaRules.EnumerableTableAccessRel(cluster,
+        cluster.traitSetOf(EnumerableConvention.INSTANCE), this, elementType);
+    if (table instanceof FilterableTable
+        || table instanceof ProjectableFilterableTable) {
+      return new JavaRules.EnumerableInterpreterRel(cluster, scan.getTraitSet(),
+          scan, 1d);
+    }
+    return scan;
   }
 
   private Class deduceElementType() {
@@ -184,6 +197,10 @@ public class RelOptTableImpl implements Prepare.PreparingTable {
       } else {
         return Object[].class;
       }
+    } else if (table instanceof ScannableTable
+        || table instanceof FilterableTable
+        || table instanceof ProjectableFilterableTable) {
+      return Object[].class;
     } else {
       return Object.class;
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRel.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRel.java b/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRel.java
index 73d5ad2..5a69057 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRel.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRel.java
@@ -18,7 +18,16 @@ package net.hydromatic.optiq.rules.java;
 
 import net.hydromatic.linq4j.expressions.BlockStatement;
 
+import org.eigenbase.rel.ProjectRelBase;
+import org.eigenbase.rel.RelFactories;
 import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.rex.RexUtil;
+import org.eigenbase.sql.validate.SqlValidatorUtil;
+
+import java.util.List;
 
 /**
  * A relational expression of one of the
@@ -27,6 +36,30 @@ import org.eigenbase.rel.RelNode;
  */
 public interface EnumerableRel
     extends RelNode {
+  RelFactories.FilterFactory FILTER_FACTORY =
+      new RelFactories.FilterFactory() {
+        public RelNode createFilter(RelNode child, RexNode condition) {
+          return new JavaRules.EnumerableFilterRel(child.getCluster(),
+              child.getTraitSet(), child, condition);
+        }
+      };
+
+  RelFactories.ProjectFactory PROJECT_FACTORY =
+      new RelFactories.ProjectFactory() {
+        public RelNode createProject(RelNode child,
+            List<? extends RexNode> exprs, List<String> fieldNames) {
+          final RelOptCluster cluster = child.getCluster();
+          final RelDataType rowType =
+              RexUtil.createStructType(cluster.getTypeFactory(), exprs,
+                  fieldNames == null ? null
+                      : SqlValidatorUtil.uniquify(fieldNames,
+                          SqlValidatorUtil.F_SUGGESTER));
+          return new JavaRules.EnumerableProjectRel(cluster,
+              child.getTraitSet(), child, exprs, rowType,
+              ProjectRelBase.Flags.BOXED);
+        }
+      };
+
   //~ Methods ----------------------------------------------------------------
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRelImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRelImplementor.java b/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRelImplementor.java
index 8d818d5..135582e 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRelImplementor.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/EnumerableRelImplementor.java
@@ -25,6 +25,7 @@ import net.hydromatic.optiq.DataContext;
 import net.hydromatic.optiq.jdbc.JavaTypeFactoryImpl;
 import net.hydromatic.optiq.runtime.*;
 
+import org.eigenbase.rel.RelNode;
 import org.eigenbase.rex.RexBuilder;
 
 import com.google.common.collect.ImmutableList;
@@ -40,11 +41,12 @@ import java.util.*;
  * operators of {@link EnumerableConvention} calling convention.
  */
 public class EnumerableRelImplementor extends JavaRelImplementor {
-  public final Map<String, Queryable> map =
-      new LinkedHashMap<String, Queryable>();
+  public final Map<String, Object> map;
 
-  public EnumerableRelImplementor(RexBuilder rexBuilder) {
+  public EnumerableRelImplementor(RexBuilder rexBuilder,
+      Map<String, Object> internalParameters) {
     super(rexBuilder);
+    this.map = internalParameters;
   }
 
   public EnumerableRel.Result visitChild(
@@ -88,19 +90,13 @@ public class EnumerableRelImplementor extends JavaRelImplementor {
             BuiltinMethod.BINDABLE_BIND.method.getName(),
             Expressions.list(root0_),
             block));
-    memberDeclarations.add(
-        Expressions.methodDecl(
-            Modifier.PUBLIC,
-            Type.class,
-            BuiltinMethod.TYPED_GET_ELEMENT_TYPE.method.getName(),
-            Collections.<ParameterExpression>emptyList(),
-            Blocks.toFunctionBlock(
-                Expressions.return_(
-                    null,
-                    Expressions.constant(
-                        result.physType.getJavaRowType())))));
-    return Expressions.classDecl(
-        Modifier.PUBLIC,
+    memberDeclarations.add(Expressions.methodDecl(Modifier.PUBLIC,
+        Type.class,
+        BuiltinMethod.TYPED_GET_ELEMENT_TYPE.method.getName(),
+        Collections.<ParameterExpression>emptyList(),
+        Blocks.toFunctionBlock(Expressions.return_(null,
+            Expressions.constant(result.physType.getJavaRowType())))));
+    return Expressions.classDecl(Modifier.PUBLIC,
         "Baz",
         null,
         Collections.<Type>singletonList(Bindable.class),
@@ -355,9 +351,20 @@ public class EnumerableRelImplementor extends JavaRelImplementor {
   }
 
   public Expression register(Queryable queryable) {
-    String name = "v" + map.size();
-    map.put(name, queryable);
-    return Expressions.variable(queryable.getClass(), name);
+    return register(queryable, queryable.getClass());
+  }
+
+  public ParameterExpression register(Object o, Class clazz) {
+    final String name = "v" + map.size();
+    map.put(name, o);
+    return Expressions.variable(clazz, name);
+  }
+
+  public Expression stash(RelNode child, Class clazz) {
+    final ParameterExpression x = register(child, clazz);
+    final Expression e = Expressions.call(getRootExpression(),
+        BuiltinMethod.DATA_CONTEXT_GET.method, Expressions.constant(x.name));
+    return Expressions.convert_(e, clazz);
   }
 
   public EnumerableRel.Result result(PhysType physType, BlockStatement block) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java b/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
index d9ead6c..f6a4f0d 100644
--- a/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
+++ b/core/src/main/java/net/hydromatic/optiq/rules/java/JavaRules.java
@@ -22,7 +22,10 @@ import net.hydromatic.linq4j.expressions.Expression;
 import net.hydromatic.linq4j.function.*;
 
 import net.hydromatic.optiq.*;
+import net.hydromatic.optiq.impl.interpreter.Interpreter;
+import net.hydromatic.optiq.impl.interpreter.Row;
 import net.hydromatic.optiq.impl.java.JavaTypeFactory;
+import net.hydromatic.optiq.jdbc.JavaTypeFactoryImpl;
 import net.hydromatic.optiq.prepare.OptiqPrepareImpl;
 import net.hydromatic.optiq.prepare.Prepare;
 import net.hydromatic.optiq.rules.java.impl.*;
@@ -508,38 +511,76 @@ public class JavaRules {
       this.elementType = elementType;
     }
 
-    private Expression getExpression() {
-      Expression expression = table.getExpression(Queryable.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;
+    }
+
+    private Expression toEnumerable(Expression expression) {
       final Type type = expression.getType();
       if (Types.isArray(type)) {
         if (Types.toClass(type).getComponentType().isPrimitive()) {
           expression =
-              Expressions.call(
-                  BuiltinMethod.AS_LIST.method,
-                  expression);
+              Expressions.call(BuiltinMethod.AS_LIST.method, expression);
         }
-        expression =
-            Expressions.call(
-                BuiltinMethod.AS_ENUMERABLE.method,
-                expression);
+        return Expressions.call(BuiltinMethod.AS_ENUMERABLE.method, expression);
       } else if (Types.isAssignableFrom(Iterable.class, type)
           && !Types.isAssignableFrom(Enumerable.class, type)) {
-        expression =
-            Expressions.call(
-                BuiltinMethod.AS_ENUMERABLE2.method,
-                expression);
+        return Expressions.call(BuiltinMethod.AS_ENUMERABLE2.method,
+            expression);
       } else if (Types.isAssignableFrom(Queryable.class, type)) {
         // Queryable extends Enumerable, but it's too "clever", so we call
         // Queryable.asEnumerable so that operations such as take(int) will be
         // evaluated directly.
-        expression =
-            Expressions.call(
-                expression,
-                BuiltinMethod.QUERYABLE_AS_ENUMERABLE.method);
+        return Expressions.call(expression,
+            BuiltinMethod.QUERYABLE_AS_ENUMERABLE.method);
       }
       return expression;
     }
 
+    private Expression toRows(PhysType physType, Expression expression) {
+      final ParameterExpression row_ =
+          Expressions.parameter(elementType, "row");
+      List<Expression> expressionList = new ArrayList<Expression>();
+      final int fieldCount = table.getRowType().getFieldCount();
+      if (elementType == Row.class) {
+        // Convert Enumerable<Row> to Enumerable<SyntheticRecord>
+        for (int i = 0; i < fieldCount; i++) {
+          expressionList.add(
+              RexToLixTranslator.convert(
+                  Expressions.call(row_,
+                      BuiltinMethod.ROW_VALUE.method,
+                      Expressions.constant(i)),
+                  physType.getJavaFieldType(i)));
+        }
+      } else if (elementType == Object[].class
+          && rowType.getFieldCount() == 1) {
+        // Convert Enumerable<Object[]> to Enumerable<SyntheticRecord>
+        for (int i = 0; i < fieldCount; i++) {
+          expressionList.add(
+              RexToLixTranslator.convert(
+                  Expressions.arrayIndex(row_, Expressions.constant(i)),
+                  physType.getJavaFieldType(i)));
+        }
+      } else if (elementType == Object.class) {
+        if (!(physType.getJavaRowType()
+            instanceof JavaTypeFactoryImpl.SyntheticRecordType)) {
+          return expression;
+        }
+        expressionList.add(
+            RexToLixTranslator.convert(row_, physType.getJavaFieldType(0)));
+      } else {
+        return expression;
+      }
+      return Expressions.call(expression,
+          BuiltinMethod.SELECT.method,
+          Expressions.lambda(Function1.class, physType.record(expressionList),
+              row_));
+    }
+
     private JavaRowFormat format() {
       if (Object[].class.isAssignableFrom(elementType)) {
         return JavaRowFormat.ARRAY;
@@ -564,11 +605,55 @@ public class JavaRules {
               implementor.getTypeFactory(),
               getRowType(),
               format());
-      final Expression expression = getExpression();
+      final Expression expression = getExpression(physType);
       return implementor.result(physType, Blocks.toBlock(expression));
     }
   }
 
+  /** Relational expression that executes its children using an interpreter.
+   *
+   * <p>Although quite a few kinds of {@link RelNode} can be interpreted,
+   * this is only created by default for {@link FilterableTable}
+   * and {@link ProjectableFilterableTable}.
+   */
+  public static class EnumerableInterpreterRel extends SingleRel
+      implements EnumerableRel {
+    private final double factor;
+
+    /** Creates an EnumerableInterpreterRel. */
+    public EnumerableInterpreterRel(RelOptCluster cluster,
+        RelTraitSet traitSet, RelNode input, double factor) {
+      super(cluster, traitSet, input);
+      this.factor = factor;
+    }
+
+    @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+      return super.computeSelfCost(planner).multiplyBy(factor);
+    }
+
+    @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+      return new EnumerableInterpreterRel(getCluster(), traitSet, sole(inputs),
+          factor);
+    }
+
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+      final JavaTypeFactory typeFactory = implementor.getTypeFactory();
+      final BlockBuilder builder = new BlockBuilder();
+      final PhysType physType =
+          PhysTypeImpl.of(typeFactory, getRowType(), JavaRowFormat.ARRAY);
+      final Expression interpreter_ = builder.append("interpreter",
+          Expressions.new_(Interpreter.class,
+              implementor.getRootExpression(),
+              implementor.stash(getChild(), RelNode.class)));
+      final Expression sliced_ =
+          getRowType().getFieldCount() == 1
+              ? Expressions.call(BuiltinMethod.SLICE0.method, interpreter_)
+              : interpreter_;
+      builder.add(sliced_);
+      return implementor.result(physType, builder.toBlock());
+    }
+  }
+
   public static final EnumerableProjectRule ENUMERABLE_PROJECT_RULE =
       new EnumerableProjectRule();
 
@@ -616,7 +701,7 @@ public class JavaRules {
         RelOptCluster cluster,
         RelTraitSet traitSet,
         RelNode child,
-        List<RexNode> exps,
+        List<? extends RexNode> exps,
         RelDataType rowType,
         int flags) {
       super(cluster, traitSet, child, exps, rowType, flags);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/runtime/Enumerables.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/runtime/Enumerables.java b/core/src/main/java/net/hydromatic/optiq/runtime/Enumerables.java
index 00a5de8..92b14d6 100644
--- a/core/src/main/java/net/hydromatic/optiq/runtime/Enumerables.java
+++ b/core/src/main/java/net/hydromatic/optiq/runtime/Enumerables.java
@@ -23,6 +23,8 @@ import net.hydromatic.linq4j.function.EqualityComparer;
 import net.hydromatic.linq4j.function.Function1;
 import net.hydromatic.linq4j.function.Predicate1;
 
+import net.hydromatic.optiq.impl.interpreter.Row;
+
 import org.eigenbase.util.Bug;
 
 /**
@@ -33,9 +35,30 @@ import org.eigenbase.util.Bug;
  * Methods are subject to removal without notice.</p>
  */
 public class Enumerables {
+  private static final Function1<?, ?> SLICE =
+      new Function1<Object[], Object>() {
+        public Object apply(Object[] a0) {
+          return a0[0];
+        }
+      };
+
+  private static final Function1<Object[], Row> ARRAY_TO_ROW =
+      new Function1<Object[], Row>() {
+        public Row apply(Object[] a0) {
+          return Row.asCopy(a0);
+        }
+      };
+
   private Enumerables() {}
 
-  /**
+  /** Converts an enumerable over singleton arrays into the enumerable of their
+   * first elements. */
+  public static <E> Enumerable<E> slice0(Enumerable<E[]> enumerable) {
+    //noinspection unchecked
+    return enumerable.select((Function1<E[], E>) SLICE);
+  }
+
+   /**
    * Returns elements of {@code outer} for which there is a member of
    * {@code inner} with a matching key.
    */
@@ -117,6 +140,11 @@ public class Enumerables {
     };
   }
 
+  /** Converts an {@link Enumerable} over object arrays into an
+   * {@link Enumerable} over {@link Row} objects. */
+  public static Enumerable<Row> toRow(final Enumerable<Object[]> enumerator) {
+    return enumerator.select(ARRAY_TO_ROW);
+  }
 }
 
 // End Enumerables.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/runtime/EnumeratorCursor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/runtime/EnumeratorCursor.java b/core/src/main/java/net/hydromatic/optiq/runtime/EnumeratorCursor.java
index 30061e6..4ce8d1c 100644
--- a/core/src/main/java/net/hydromatic/optiq/runtime/EnumeratorCursor.java
+++ b/core/src/main/java/net/hydromatic/optiq/runtime/EnumeratorCursor.java
@@ -26,7 +26,7 @@ import net.hydromatic.linq4j.Enumerator;
  * For instance,
  * {@link net.hydromatic.optiq.rules.java.JavaRules.EnumerableCalcRel}
  * computes result just in {@code current()} method, thus it makes sense to
- * cache the result and make it available for all the accesors.
+ * cache the result and make it available for all the accessors.
  *
  * @param <T> Element type
  */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
index a0fd46b..1364467 100644
--- a/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
+++ b/core/src/main/java/net/hydromatic/optiq/tools/Programs.java
@@ -91,6 +91,7 @@ public class Programs {
               : MergeProjectRule.INSTANCE,
           AggregateStarTableRule.INSTANCE,
           AggregateStarTableRule.INSTANCE2,
+          FilterTableRule.INSTANCE,
           PushFilterPastProjectRule.INSTANCE,
           PushFilterPastJoinRule.FILTER_ON_JOIN,
           RemoveDistinctAggregateRule.INSTANCE,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/org/eigenbase/rel/metadata/RelMdPercentageOriginalRows.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/metadata/RelMdPercentageOriginalRows.java b/core/src/main/java/org/eigenbase/rel/metadata/RelMdPercentageOriginalRows.java
index 803c905..7b4a677 100644
--- a/core/src/main/java/org/eigenbase/rel/metadata/RelMdPercentageOriginalRows.java
+++ b/core/src/main/java/org/eigenbase/rel/metadata/RelMdPercentageOriginalRows.java
@@ -22,6 +22,7 @@ import org.eigenbase.rel.*;
 import org.eigenbase.relopt.*;
 
 import net.hydromatic.optiq.BuiltinMethod;
+import net.hydromatic.optiq.rules.java.JavaRules;
 
 import com.google.common.collect.ImmutableList;
 
@@ -153,6 +154,10 @@ public class RelMdPercentageOriginalRows {
     return cost;
   }
 
+  public RelOptCost getCumulativeCost(JavaRules.EnumerableInterpreterRel rel) {
+    return RelMetadataQuery.getNonCumulativeCost(rel);
+  }
+
   // Ditto for getNonCumulativeCost
   public RelOptCost getNonCumulativeCost(RelNode rel) {
     return rel.computeSelfCost(rel.getCluster().getPlanner());

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/org/eigenbase/rel/rules/FilterTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/FilterTableRule.java b/core/src/main/java/org/eigenbase/rel/rules/FilterTableRule.java
new file mode 100644
index 0000000..c168ef9
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/rel/rules/FilterTableRule.java
@@ -0,0 +1,168 @@
+/*
+ * 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.eigenbase.rel.rules;
+
+import java.util.List;
+
+import org.eigenbase.rel.FilterRelBase;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.TableAccessRelBase;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelOptUtil;
+import org.eigenbase.rex.*;
+
+import net.hydromatic.linq4j.Enumerable;
+
+import net.hydromatic.optiq.DataContext;
+import net.hydromatic.optiq.FilterableTable;
+import net.hydromatic.optiq.ProjectableFilterableTable;
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.JavaRules;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import static org.eigenbase.util.Static.RESOURCE;
+
+/**
+ * Planner rule that pushes a filter into a scan of a {@link FilterableTable}
+ * or {@link net.hydromatic.optiq.ProjectableFilterableTable}.
+ */
+public class FilterTableRule extends RelOptRule {
+  private static final Predicate<TableAccessRelBase> PREDICATE =
+      new Predicate<TableAccessRelBase>() {
+        public boolean apply(TableAccessRelBase scan) {
+          // We can only push filters into a FilterableTable or
+          // ProjectableFilterableTable.
+          final RelOptTable table = scan.getTable();
+          return table.unwrap(FilterableTable.class) != null
+              || table.unwrap(ProjectableFilterableTable.class) != null;
+        }
+      };
+
+  public static final FilterTableRule INSTANCE = new FilterTableRule();
+
+  //~ Constructors -----------------------------------------------------------
+
+  /** Creates a FilterTableRule. */
+  private FilterTableRule() {
+    super(
+        operand(FilterRelBase.class,
+            operand(JavaRules.EnumerableInterpreterRel.class,
+                operand(TableAccessRelBase.class, null, PREDICATE, none()))));
+  }
+
+  //~ Methods ----------------------------------------------------------------
+
+  // implement RelOptRule
+  public void onMatch(RelOptRuleCall call) {
+    final FilterRelBase filter = call.rel(0);
+    final JavaRules.EnumerableInterpreterRel interpreter = call.rel(1);
+    final TableAccessRelBase scan = call.rel(2);
+    final FilterableTable filterableTable =
+        scan.getTable().unwrap(FilterableTable.class);
+    final ProjectableFilterableTable projectableFilterableTable =
+        scan.getTable().unwrap(ProjectableFilterableTable.class);
+
+    final FilterSplit filterSplit;
+    if (filterableTable != null) {
+      filterSplit = FilterSplit.of(filterableTable, filter.getCondition(),
+          null);
+    } else if (projectableFilterableTable != null) {
+      filterSplit = FilterSplit.of(projectableFilterableTable,
+          filter.getCondition(), null);
+    } else {
+      throw new AssertionError(scan.getTable());
+    }
+
+    // It's worth using the ProjectableFilterableTable interface even if it
+    // refused all filters.
+    final RelNode newFilter =
+        RelOptUtil.createFilter(interpreter.getChild(),
+            filterSplit.acceptedFilters, EnumerableRel.FILTER_FACTORY);
+    final RelNode newInterpreter =
+        new JavaRules.EnumerableInterpreterRel(interpreter.getCluster(),
+            interpreter.getTraitSet(), newFilter, 0.15d);
+    final RelNode residue =
+        RelOptUtil.createFilter(newInterpreter, filterSplit.rejectedFilters);
+    call.transformTo(residue);
+  }
+
+  /** Splits a filter condition into parts that can and cannot be
+   * handled by a {@link FilterableTable} or
+   * {@link ProjectableFilterableTable}. */
+  public static class FilterSplit {
+    public final ImmutableList<RexNode> acceptedFilters;
+    public final ImmutableList<RexNode> rejectedFilters;
+
+    public FilterSplit(ImmutableList<RexNode> acceptedFilters,
+        ImmutableList<RexNode> rejectedFilters) {
+      this.acceptedFilters = acceptedFilters;
+      this.rejectedFilters = rejectedFilters;
+    }
+
+    public static FilterSplit of(FilterableTable table,
+        RexNode condition, DataContext dataContext) {
+      final List<RexNode> filters = Lists.newArrayList();
+      RelOptUtil.decomposeConjunction(condition, filters);
+      final List<RexNode> originalFilters = ImmutableList.copyOf(filters);
+
+      final Enumerable<Object[]> enumerable =
+          table.scan(dataContext, filters);
+      return rest(originalFilters, filters, enumerable);
+    }
+
+    public static FilterSplit of(ProjectableFilterableTable table,
+        RexNode condition, DataContext dataContext) {
+      final List<RexNode> filters = Lists.newArrayList();
+      RelOptUtil.decomposeConjunction(condition, filters);
+      final List<RexNode> originalFilters = ImmutableList.copyOf(filters);
+
+      final Enumerable<Object[]> enumerable =
+          table.scan(dataContext, filters, null);
+      return rest(originalFilters, filters, enumerable);
+    }
+
+    private static FilterSplit rest(List<RexNode> originalFilters,
+        List<RexNode> filters,
+        Enumerable<Object[]> enumerable) {
+      if (enumerable == null) {
+        throw RESOURCE.filterableTableScanReturnedNull().ex();
+      }
+      final ImmutableList.Builder<RexNode> accepted = ImmutableList.builder();
+      final ImmutableList.Builder<RexNode> rejected = ImmutableList.builder();
+      for (RexNode originalFilter : originalFilters) {
+        if (filters.contains(originalFilter)) {
+          rejected.add(originalFilter);
+        } else {
+          accepted.add(originalFilter);
+        }
+      }
+      for (RexNode node : filters) {
+        if (!originalFilters.contains(node)) {
+          throw RESOURCE.filterableTableInventedFilter(node.toString()).ex();
+        }
+      }
+      return new FilterSplit(accepted.build(), rejected.build());
+    }
+  }
+}
+
+// End FilterTableRule.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/263640ff/core/src/main/java/org/eigenbase/rel/rules/ProjectTableRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/eigenbase/rel/rules/ProjectTableRule.java b/core/src/main/java/org/eigenbase/rel/rules/ProjectTableRule.java
new file mode 100644
index 0000000..475edbb
--- /dev/null
+++ b/core/src/main/java/org/eigenbase/rel/rules/ProjectTableRule.java
@@ -0,0 +1,167 @@
+/*
+ * 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.eigenbase.rel.rules;
+
+import java.util.List;
+
+import org.eigenbase.rel.FilterRelBase;
+import org.eigenbase.rel.ProjectRelBase;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.TableAccessRelBase;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelOptRuleOperand;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelOptUtil;
+import org.eigenbase.rex.RexBuilder;
+import org.eigenbase.rex.RexInputRef;
+import org.eigenbase.rex.RexLocalRef;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.rex.RexProgram;
+import org.eigenbase.rex.RexShuttle;
+
+import net.hydromatic.optiq.ProjectableFilterableTable;
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.JavaRules;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Lists;
+
+/**
+ * Planner rule that pushes a project into a scan of a
+ * {@link net.hydromatic.optiq.ProjectableFilterableTable}.
+ *
+ * @see org.eigenbase.rel.rules.FilterTableRule
+ */
+public abstract class ProjectTableRule extends RelOptRule {
+  private static final Predicate<TableAccessRelBase> PREDICATE =
+      new Predicate<TableAccessRelBase>() {
+        public boolean apply(TableAccessRelBase scan) {
+          // We can only push projects into a ProjectableFilterableTable.
+          final RelOptTable table = scan.getTable();
+          return table.unwrap(ProjectableFilterableTable.class) != null;
+        }
+      };
+
+  public static final ProjectTableRule INSTANCE =
+      new ProjectTableRule(
+          operand(ProjectRelBase.class,
+              operand(JavaRules.EnumerableInterpreterRel.class,
+                  operand(TableAccessRelBase.class, null, PREDICATE, none()))),
+          "ProjectTableRule:basic") {
+        @Override public void onMatch(RelOptRuleCall call) {
+          final ProjectRelBase project = call.rel(0);
+          final JavaRules.EnumerableInterpreterRel interpreter = call.rel(1);
+          final TableAccessRelBase scan = call.rel(2);
+          final RelOptTable table = scan.getTable();
+          assert table.unwrap(ProjectableFilterableTable.class) != null;
+          apply(call, project, null, interpreter);
+        }
+      };
+
+  public static final ProjectTableRule INSTANCE2 =
+      new ProjectTableRule(
+          operand(ProjectRelBase.class,
+              operand(FilterRelBase.class,
+                  operand(JavaRules.EnumerableInterpreterRel.class,
+                      operand(TableAccessRelBase.class, null, PREDICATE,
+                          none())))),
+          "ProjectTableRule:filter") {
+        @Override public void onMatch(RelOptRuleCall call) {
+          final ProjectRelBase project = call.rel(0);
+          final FilterRelBase filter = call.rel(1);
+          final JavaRules.EnumerableInterpreterRel interpreter = call.rel(2);
+          final TableAccessRelBase scan = call.rel(3);
+          final RelOptTable table = scan.getTable();
+          assert table.unwrap(ProjectableFilterableTable.class) != null;
+          apply(call, project, filter, interpreter);
+        }
+      };
+
+  //~ Constructors -----------------------------------------------------------
+
+  /** Creates a FilterTableRule. */
+  private ProjectTableRule(RelOptRuleOperand operand, String description) {
+    super(operand, description);
+  }
+
+  //~ Methods ----------------------------------------------------------------
+
+  protected void apply(RelOptRuleCall call, ProjectRelBase project,
+      FilterRelBase filter, JavaRules.EnumerableInterpreterRel interpreter) {
+    // Split the projects into column references and expressions on top of them.
+    // Creating a RexProgram is a convenient way to do this.
+    final RexBuilder rexBuilder = project.getCluster().getRexBuilder();
+    final RexProgram program = RexProgram.create(interpreter.getRowType(),
+        project.getProjects(), null, project.getRowType(), rexBuilder);
+    final List<Integer> projectOrdinals = Lists.newArrayList();
+    final List<RexNode> extraProjects;
+    if (program.getExprList().size()
+        == program.getInputRowType().getFieldCount()) {
+      // There are only field references, no non-trivial expressions.
+      for (RexLocalRef ref : program.getProjectList()) {
+        projectOrdinals.add(ref.getIndex());
+      }
+      extraProjects = null;
+    } else {
+      extraProjects = Lists.newArrayList();
+      RexShuttle shuttle = new RexShuttle() {
+        final List<RexInputRef> inputRefs = Lists.newArrayList();
+
+        @Override public RexNode visitInputRef(RexInputRef inputRef) {
+          final int source = inputRef.getIndex();
+          int target = projectOrdinals.indexOf(source);
+          final RexInputRef ref;
+          if (target < 0) {
+            target = projectOrdinals.size();
+            projectOrdinals.add(source);
+            ref = rexBuilder.makeInputRef(inputRef.getType(), target);
+            inputRefs.add(ref);
+          } else {
+            ref = inputRefs.get(target);
+          }
+          return ref;
+        }
+      };
+      for (RexNode node : project.getProjects()) {
+        extraProjects.add(node.accept(shuttle));
+      }
+    }
+
+    RelNode input = interpreter.getChild();
+    if (filter != null) {
+      input = RelOptUtil.createFilter(input, filter.getCondition(),
+          EnumerableRel.FILTER_FACTORY);
+    }
+    final RelNode newProject =
+        RelOptUtil.createProject(EnumerableRel.PROJECT_FACTORY, input,
+            projectOrdinals);
+    final RelNode newInterpreter =
+        new JavaRules.EnumerableInterpreterRel(interpreter.getCluster(),
+            interpreter.getTraitSet(), newProject, 0.15d);
+    final RelNode residue;
+    if (extraProjects != null) {
+      residue = RelOptUtil.createProject(newInterpreter, extraProjects,
+          project.getRowType().getFieldNames());
+    } else {
+      residue = newInterpreter;
+    }
+    call.transformTo(residue);
+  }
+}
+
+// End ProjectTableRule.java


Mime
View raw message