phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From maryann...@apache.org
Subject [1/2] phoenix git commit: Sync changes from https://github.com/julianhyde/phoenix/tree/calcite
Date Tue, 02 Dec 2014 21:51:23 GMT
Repository: phoenix
Updated Branches:
  refs/heads/calcite 766eec2b5 -> b005b1246


Sync changes from https://github.com/julianhyde/phoenix/tree/calcite


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/76a858a4
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/76a858a4
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/76a858a4

Branch: refs/heads/calcite
Commit: 76a858a411b0a497037f1c5384a4cd9ca838a991
Parents: 766eec2
Author: maryannxue <maryannxue@apache.org>
Authored: Tue Dec 2 16:49:09 2014 -0500
Committer: maryannxue <maryannxue@apache.org>
Committed: Tue Dec 2 16:49:09 2014 -0500

----------------------------------------------------------------------
 .../org/apache/phoenix/calcite/CalciteTest.java | 226 +++++++++++++
 .../apache/phoenix/calcite/BuiltInMethod.java   |  44 +++
 .../apache/phoenix/calcite/CalciteRuntime.java  |  79 +++++
 .../apache/phoenix/calcite/CalciteUtils.java    |  90 +++++
 .../phoenix/calcite/PhoenixAggregate.java       |  53 +++
 .../apache/phoenix/calcite/PhoenixFilter.java   |  40 +++
 .../calcite/PhoenixFilterScanMergeRule.java     |  36 ++
 .../org/apache/phoenix/calcite/PhoenixJoin.java |  40 +++
 .../apache/phoenix/calcite/PhoenixProject.java  |  40 +++
 .../org/apache/phoenix/calcite/PhoenixRel.java  |  39 +++
 .../calcite/PhoenixRelImplementorImpl.java      |  91 +++++
 .../apache/phoenix/calcite/PhoenixRules.java    | 334 +++++++++++++++++++
 .../apache/phoenix/calcite/PhoenixSchema.java   | 135 ++++++++
 .../org/apache/phoenix/calcite/PhoenixSort.java |  32 ++
 .../apache/phoenix/calcite/PhoenixTable.java    |  77 +++++
 .../phoenix/calcite/PhoenixTableScan.java       |  60 ++++
 .../calcite/PhoenixToEnumerableConverter.java   |  72 ++++
 .../apache/phoenix/calcite/PhoenixUnion.java    |  39 +++
 .../apache/phoenix/calcite/PhoenixValues.java   |  36 ++
 .../phoenix/calcite/ToExpressionTest.java       |  77 +++++
 20 files changed, 1640 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
new file mode 100644
index 0000000..9bce0a3
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
@@ -0,0 +1,226 @@
+package org.apache.phoenix.calcite;
+
+import com.google.common.collect.Lists;
+import org.apache.calcite.jdbc.CalciteConnection;
+import org.apache.phoenix.end2end.BaseClientManagedTimeIT;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.BaseTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.sql.*;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.*;
+
+/**
+ * Integration test for queries powered by Calcite.
+ */
+public class CalciteTest extends BaseClientManagedTimeIT {
+    public static final String ATABLE_NAME = "ATABLE";
+
+    public static Start start() {
+        return new Start();
+    }
+
+    public static class Start {
+        private Connection connection;
+
+        Connection createConnection() throws Exception {
+            return CalciteTest.createConnection();
+        }
+
+        public Sql sql(String sql) {
+            return new Sql(this, sql);
+        }
+
+        public Connection getConnection() {
+            if (connection == null) {
+                try {
+                    connection = createConnection();
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            return connection;
+        }
+
+        public void close() {
+            if (connection != null) {
+                try {
+                    connection.close();
+                } catch (SQLException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    /** Fluid class for a test that has specified a SQL query. */
+    static class Sql {
+        private final Start start;
+        private final String sql;
+
+        public Sql(Start start, String sql) {
+            this.start = start;
+            this.sql = sql;
+        }
+
+        public List<String> getResult(ResultSet resultSet) throws SQLException {
+            final List<String> list = Lists.newArrayList();
+            populateResult(resultSet, list);
+            return list;
+        }
+
+        private void populateResult(ResultSet resultSet, List<String> list) throws SQLException {
+            final StringBuilder buf = new StringBuilder();
+            final int columnCount = resultSet.getMetaData().getColumnCount();
+            while (resultSet.next()) {
+                for (int i = 0; i < columnCount; i++) {
+                    if (i > 0) {
+                        buf.append(", ");
+                    }
+                    buf.append(resultSet.getString(i + 1));
+                }
+                list.add(buf.toString());
+                buf.setLength(0);
+            }
+        }
+
+        public Sql explainIs(String expected) {
+            final List<String> list = getResult("explain plan for " + sql);
+            if (list.size() != 1) {
+                fail("explain should return 1 row, got " + list.size());
+            }
+            String explain = list.get(0);
+            assertThat(explain, equalTo(expected));
+            return this;
+        }
+
+        public List<String> getResult(String sql) {
+            try {
+                final Statement statement = start.getConnection().createStatement();
+                final ResultSet resultSet = statement.executeQuery(sql);
+                List<String> list = getResult(resultSet);
+                resultSet.close();
+                statement.close();
+                return list;
+            } catch (SQLException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void close() {
+            start.close();
+        }
+
+        public Sql resultIs(String... lines) {
+            assertThat(Arrays.asList(lines), equalTo(getResult(sql)));
+            return this;
+        }
+    }
+
+    private static Connection createConnection() throws SQLException {
+        final Connection connection = DriverManager.getConnection(
+            "jdbc:calcite:");
+        final CalciteConnection calciteConnection =
+            connection.unwrap(CalciteConnection.class);
+        final String url = getUrl();
+        final PhoenixConnection phoenixConnection =
+            DriverManager.getConnection(url).unwrap(PhoenixConnection.class);
+        BaseTest.ensureTableCreated(url, ATABLE_NAME);
+        calciteConnection.getRootSchema().add("phoenix",
+            new PhoenixSchema(phoenixConnection));
+        calciteConnection.setSchema("phoenix");
+        return connection;
+    }
+
+    private static Connection connectUsingModel() throws Exception {
+        final File file = File.createTempFile("model", ".json");
+        final String url = getUrl();
+        final PrintWriter pw = new PrintWriter(new FileWriter(file));
+        pw.print(
+            "{\n"
+                + "  version: '1.0',\n"
+                + "  defaultSchema: 'HR',\n"
+                + "  schemas: [\n"
+                + "    {\n"
+                + "      name: 'HR',\n"
+                + "      type: 'custom',\n"
+                + "      factory: 'org.apache.phoenix.calcite.PhoenixSchema$Factory',\n"
+                + "      operand: {\n"
+                + "        url: \"" + url + "\",\n"
+                + "        user: \"scott\",\n"
+                + "        password: \"tiger\"\n"
+                + "      }\n"
+                + "    }\n"
+                + "  ]\n"
+                + "}\n");
+        pw.close();
+        final Connection connection =
+            DriverManager.getConnection("jdbc:calcite:model=" + file.getAbsolutePath());
+        BaseTest.ensureTableCreated(url, ATABLE_NAME);
+        return connection;
+    }
+
+    @Test public void testConnect() throws Exception {
+        final Connection connection = DriverManager.getConnection("jdbc:calcite:");
+        final CalciteConnection calciteConnection =
+            connection.unwrap(CalciteConnection.class);
+        Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
+        final String url = getUrl();
+        final PhoenixConnection phoenixConnection =
+            DriverManager.getConnection(url).unwrap(PhoenixConnection.class);
+        ensureTableCreated(url, ATABLE_NAME);
+        initATableValues(getOrganizationId(), null, url);
+        calciteConnection.getRootSchema().add("phoenix",
+            new PhoenixSchema(phoenixConnection));
+        calciteConnection.setSchema("phoenix");
+        final Statement statement = calciteConnection.createStatement();
+        final ResultSet resultSet = statement.executeQuery("select * from aTable where a_string = 'a'");
+        while (resultSet.next()) {
+            System.out.println("org_id=" + resultSet.getObject(1) + ",entity_id=" + resultSet.getObject(2) + ",a_string=" + resultSet.getObject("A_STRING"));
+        }
+        resultSet.close();
+        statement.close();
+        connection.close();
+    }
+
+    @Test public void testExplainPlanForSelectWhereQuery() {
+        start()
+            .sql("select * from aTable where a_string = 'a'")
+            .explainIs(
+                "PhoenixToEnumerableConverter\n"
+                    + "  PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[=($2, 'a')])\n")
+            .close();
+    }
+
+    @Test public void testExplainProject() {
+        start()
+            .sql("select a_string, b_string from aTable where a_string = 'a'")
+            .explainIs(
+                "PhoenixToEnumerableConverter\n"
+                    + "  PhoenixProject(A_STRING=[$2], B_STRING=[$3])\n"
+                    + "    PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[=($2, 'a')])\n")
+            .close();
+    }
+
+    @Test public void testConnectUsingModel() throws Exception {
+        final Start start = new Start() {
+            @Override
+            Connection createConnection() throws Exception {
+                return connectUsingModel();
+            }
+        };
+        start.sql("select * from aTable")
+            .explainIs("PhoenixToEnumerableConverter\n"
+                + "  PhoenixTableScan(table=[[HR, ATABLE]])\n")
+            // .resultIs("Xx")
+            .close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/BuiltInMethod.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/BuiltInMethod.java
new file mode 100644
index 0000000..192b421
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/BuiltInMethod.java
@@ -0,0 +1,44 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.linq4j.tree.Types;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.iterate.ResultIterator;
+
+import com.google.common.collect.ImmutableMap;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Built-in methods.
+ */
+public enum BuiltInMethod {
+    RESULT_ITERATOR_NEXT(ResultIterator.class, "next"),
+    TO_ENUMERABLE(CalciteRuntime.class, "toEnumerable", QueryPlan.class);
+
+    public final Method method;
+    public final Constructor constructor;
+
+    public static final ImmutableMap<Method, BuiltInMethod> MAP;
+
+    static {
+        final ImmutableMap.Builder<Method, BuiltInMethod> builder =
+            ImmutableMap.builder();
+        for (BuiltInMethod value : BuiltInMethod.values()) {
+            if (value.method != null) {
+                builder.put(value.method, value);
+            }
+        }
+        MAP = builder.build();
+    }
+
+    BuiltInMethod(Class clazz, String methodName, Class... argumentTypes) {
+        this.method = Types.lookupMethod(clazz, methodName, argumentTypes);
+        this.constructor = null;
+    }
+
+    BuiltInMethod(Class clazz, Class... argumentTypes) {
+        this.method = null;
+        this.constructor = Types.lookupConstructor(clazz, argumentTypes);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteRuntime.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteRuntime.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteRuntime.java
new file mode 100644
index 0000000..a167ea6
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteRuntime.java
@@ -0,0 +1,79 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.linq4j.AbstractEnumerable;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.compile.ColumnProjector;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.compile.RowProjector;
+import org.apache.phoenix.iterate.ResultIterator;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+import java.sql.SQLException;
+
+/**
+ * Methods used by code generated by Calcite.
+ */
+public class CalciteRuntime {
+    public static Enumerable<Object[]> toEnumerable2(final ResultIterator iterator, final RowProjector rowProjector) {
+        return new AbstractEnumerable<Object[]>() {
+            @Override
+            public Enumerator<Object[]> enumerator() {
+                return toEnumerator(iterator, rowProjector);
+            }
+        };
+    }
+
+    public static Enumerable<Object[]> toEnumerable(final QueryPlan plan) {
+        try {
+            return toEnumerable2(plan.iterator(), plan.getProjector());
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static Enumerator<Object[]> toEnumerator(final ResultIterator iterator, final RowProjector rowProjector) {
+        return new Enumerator<Object[]>() {
+            Object[] current = new Object[rowProjector.getColumnCount()];
+            private final ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+
+            @Override
+            public Object[] current() {
+                return current;
+            }
+
+            @Override
+            public boolean moveNext() {
+                try {
+                    final Tuple tuple = iterator.next();
+                    if (tuple == null) {
+                        current = null;
+                        return false;
+                    }
+                    for (int i = 0; i < current.length; i++) {
+                        ColumnProjector projector = rowProjector.getColumnProjector(i);
+                    	current[i] = projector.getValue(tuple, projector.getExpression().getDataType(), ptr);
+                    }
+                    return true;
+                } catch (SQLException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+
+            @Override
+            public void reset() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void close() {
+                try {
+                    iterator.close();
+                } catch (SQLException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
new file mode 100644
index 0000000..956f317
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
@@ -0,0 +1,90 @@
+package org.apache.phoenix.calcite;
+
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.calcite.PhoenixRel.Implementor;
+import org.apache.phoenix.expression.ComparisonExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.ExpressionType;
+import org.apache.phoenix.expression.LiteralExpression;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+/**
+ * Utilities for interacting with Calcite.
+ */
+public class CalciteUtils {
+  private CalciteUtils() {}
+
+	private static final Map<SqlKind, ExpressionFactory> EXPRESSION_MAP = Maps
+			.newHashMapWithExpectedSize(ExpressionType.values().length);
+	private static final ExpressionFactory getFactory(RexNode node) {
+		ExpressionFactory eFactory = EXPRESSION_MAP.get(node.getKind());
+		if (eFactory == null) {
+			throw new UnsupportedOperationException("Unsupported RexNode: "
+					+ node);
+		}
+		return eFactory;
+	}
+	static {
+		EXPRESSION_MAP.put(SqlKind.EQUALS, new ExpressionFactory() {
+
+			@Override
+			public Expression newExpression(RexNode node, Implementor implementor) {
+				RexCall call = (RexCall) node;
+				List<Expression> children = Lists.newArrayListWithExpectedSize(call.getOperands().size());
+				for (RexNode op : call.getOperands()) {
+					Expression child = getFactory(op).newExpression(op, implementor);
+					children.add(child);
+				}
+				ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+				try {
+					return ComparisonExpression.create(CompareOp.EQUAL, children, ptr);
+				} catch (SQLException e) {
+					throw new RuntimeException(e);
+				}
+			}
+			
+		});
+		EXPRESSION_MAP.put(SqlKind.LITERAL, new ExpressionFactory() {
+
+			@Override
+			public Expression newExpression(RexNode node, Implementor implementor) {
+				RexLiteral lit = (RexLiteral) node;
+				Object o = lit.getValue2();
+				return LiteralExpression.newConstant(o);
+			}
+			
+		});
+		EXPRESSION_MAP.put(SqlKind.INPUT_REF, new ExpressionFactory() {
+
+			@Override
+			public Expression newExpression(RexNode node, Implementor implementor) {
+				RexInputRef ref = (RexInputRef) node;
+				int index = ref.getIndex();
+				return implementor.newColumnExpression(index);
+			}
+			
+		});
+	}
+
+	static Expression toExpression(RexNode node, Implementor implementor) {
+		ExpressionFactory eFactory = getFactory(node);
+		Expression expression = eFactory.newExpression(node, implementor);
+		return expression;
+	}
+	
+	public static interface ExpressionFactory {
+		public Expression newExpression(RexNode node, Implementor implementor);
+	}
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
new file mode 100644
index 0000000..b5e55e7
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
@@ -0,0 +1,53 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.InvalidRelException;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Aggregate}
+ * relational expression in Phoenix.
+ */
+public class PhoenixAggregate extends Aggregate implements PhoenixRel {
+    public PhoenixAggregate(RelOptCluster cluster, RelTraitSet traits, RelNode child, boolean indicator, ImmutableBitSet groupSet, List<ImmutableBitSet> groupSets, List<AggregateCall> aggCalls) throws InvalidRelException {
+        super(cluster, traits, child, indicator, groupSet, groupSets, aggCalls);
+        assert getConvention() == PhoenixRel.CONVENTION;
+        assert getConvention() == child.getConvention();
+
+        for (AggregateCall aggCall : aggCalls) {
+            if (aggCall.isDistinct()) {
+                throw new InvalidRelException( "distinct aggregation not supported");
+            }
+        }
+        switch (getGroupType()) {
+            case SIMPLE:
+                break;
+            default:
+                throw new InvalidRelException("unsupported group type: " + getGroupType());
+        }
+    }
+
+    @Override
+    public PhoenixAggregate copy(RelTraitSet traits, RelNode input, boolean indicator, ImmutableBitSet groupSet, List<ImmutableBitSet> groupSets, List<AggregateCall> aggregateCalls) {
+        try {
+            return new PhoenixAggregate(getCluster(), traits, input, indicator, groupSet, groupSets, aggregateCalls);
+        } catch (InvalidRelException e) {
+            // Semantic error not possible. Must be a bug. Convert to
+            // internal error.
+            throw new AssertionError(e);
+        }
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        implementor.visitInput(0, (PhoenixRel) getInput());
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
new file mode 100644
index 0000000..dc4bfc1
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
@@ -0,0 +1,40 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Filter}
+ * relational expression in Phoenix.
+ */
+public class PhoenixFilter extends Filter implements PhoenixRel {
+    protected PhoenixFilter(RelOptCluster cluster, RelTraitSet traits, RelNode input, RexNode condition) {
+        super(cluster, traits, input, condition);
+        assert getConvention() == PhoenixRel.CONVENTION;
+        assert getConvention() == input.getConvention();
+    }
+
+    public PhoenixFilter copy(RelTraitSet traitSet, RelNode input, RexNode condition) {
+        return new PhoenixFilter(getCluster(), traitSet, input, condition);
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(PHOENIX_FACTOR);
+    }
+
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        implementor.visitInput(0, (PhoenixRel) getInput());
+        // TODO: what to do with the Expression?
+        // Already determined this filter cannot be pushed down, so
+        // this will be run 
+        Expression expr = CalciteUtils.toExpression(condition, implementor);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
new file mode 100644
index 0000000..808fa99
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
@@ -0,0 +1,36 @@
+package org.apache.phoenix.calcite;
+
+import com.google.common.base.Predicate;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.rel.core.Filter;
+
+public class PhoenixFilterScanMergeRule extends RelOptRule {
+
+    /** Predicate that returns true if a table scan has no filter. */
+    private static final Predicate<PhoenixTableScan> NO_FILTER =
+        new Predicate<PhoenixTableScan>() {
+            @Override
+            public boolean apply(PhoenixTableScan phoenixTableScan) {
+                return phoenixTableScan.filter == null;
+            }
+        };
+
+    public static final PhoenixFilterScanMergeRule INSTANCE = new PhoenixFilterScanMergeRule();
+
+    private PhoenixFilterScanMergeRule() {
+        super(
+            operand(Filter.class,
+                operand(PhoenixTableScan.class, null, NO_FILTER, any())));
+    }
+
+    @Override
+    public void onMatch(RelOptRuleCall call) {
+        Filter filter = call.rel(0);
+        PhoenixTableScan scan = call.rel(1);
+        assert scan.filter == null : "predicate should have ensured no filter";
+        call.transformTo(new PhoenixTableScan(scan.getCluster(),
+                scan.getTraitSet(), scan.getTable(),
+                filter.getCondition()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
new file mode 100644
index 0000000..afbe604
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
@@ -0,0 +1,40 @@
+package org.apache.phoenix.calcite;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.SetOp;
+import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Join}
+ * relational expression in Phoenix.
+ */
+public class PhoenixJoin extends Join implements PhoenixRel {
+    public PhoenixJoin(RelOptCluster cluster, RelTraitSet traits, RelNode left, RelNode right, RexNode condition, JoinRelType joinType, Set<String> variablesStopped) {
+        super( cluster, traits, left, right, condition, joinType, variablesStopped);
+        assert getConvention() == PhoenixRel.CONVENTION;
+        assert left.getConvention() == PhoenixRel.CONVENTION;
+        assert right.getConvention() == PhoenixRel.CONVENTION;
+    }
+
+    @Override
+    public PhoenixJoin copy(RelTraitSet traits, RexNode condition, RelNode left, RelNode right, JoinRelType joinRelType, boolean semiJoinDone) {
+        return new PhoenixJoin(getCluster(), traits, left, right, condition, joinRelType, ImmutableSet.<String>of());
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
new file mode 100644
index 0000000..2e90397
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
@@ -0,0 +1,40 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Project}
+ * relational expression in Phoenix.
+ */
+public class PhoenixProject extends Project implements PhoenixRel {
+    public PhoenixProject(RelOptCluster cluster, RelTraitSet traits, RelNode input, List<? extends RexNode> projects, RelDataType rowType) {
+        super(cluster, traits, input, projects, rowType, Flags.BOXED);
+        assert getConvention() == PhoenixRel.CONVENTION;
+        assert getConvention() == input.getConvention();
+    }
+
+    @Override
+    public PhoenixProject copy(RelTraitSet traits, RelNode input, List<RexNode> projects, RelDataType rowType) {
+        return new PhoenixProject(getCluster(), traits, input, projects, rowType);
+    }
+
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(PHOENIX_FACTOR);
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        implementor.visitInput(0, (PhoenixRel) getInput());
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
new file mode 100644
index 0000000..c255e90
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
@@ -0,0 +1,39 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.compile.RowProjector;
+import org.apache.phoenix.expression.ColumnExpression;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.schema.PTable;
+
+/**
+ * Relational expression in Phoenix.
+ *
+ * <p>Phoenix evaluates relational expressions using {@link java.util.Iterator}s
+ * over streams of {@link org.apache.phoenix.schema.tuple.Tuple}s.</p>
+ */
+public interface PhoenixRel extends RelNode {
+  /** Calling convention for relational operations that occur in Phoenix. */
+  Convention CONVENTION = new Convention.Impl("PHOENIX", PhoenixRel.class);
+
+  /** Relative cost of Phoenix versus Enumerable convention.
+   *
+   * <p>Multiply by the value (which is less than unity), and you will get a cheaper cost.
+   * Phoenix is cheaper.
+   */
+  double PHOENIX_FACTOR = 0.5;
+
+  void implement(Implementor implementor, PhoenixConnection conn);
+
+  /** Holds context for an traversal over a tree of relational expressions
+   * to convert it to an executable plan. */
+  interface Implementor {
+    void visitInput(int i, PhoenixRel input);
+    ColumnExpression newColumnExpression(int index);
+    void setContext(PhoenixConnection conn, PTable pTable, RexNode filter);
+    QueryPlan makePlan();
+  }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
new file mode 100644
index 0000000..a7d62e2
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
@@ -0,0 +1,91 @@
+package org.apache.phoenix.calcite;
+
+import java.sql.SQLException;
+import java.util.Collections;
+
+import org.apache.calcite.rex.RexNode;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.compile.FromCompiler;
+import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
+import org.apache.phoenix.compile.ProjectionCompiler;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.compile.RowProjector;
+import org.apache.phoenix.compile.SequenceManager;
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
+import org.apache.phoenix.compile.WhereCompiler;
+import org.apache.phoenix.compile.WhereOptimizer;
+import org.apache.phoenix.execute.AggregatePlan;
+import org.apache.phoenix.execute.ScanPlan;
+import org.apache.phoenix.expression.ColumnExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.iterate.ParallelIteratorFactory;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.ColumnDef;
+import org.apache.phoenix.parse.FilterableStatement;
+import org.apache.phoenix.parse.NamedTableNode;
+import org.apache.phoenix.parse.SelectStatement;
+import org.apache.phoenix.parse.TableName;
+import org.apache.phoenix.schema.ColumnRef;
+import org.apache.phoenix.schema.PDatum;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.TableRef;
+
+import com.google.common.collect.ImmutableList;
+
+class PhoenixRelImplementorImpl implements PhoenixRel.Implementor {
+	private TableRef tableRef;
+	private PhoenixConnection conn;
+	private StatementContext context;
+	private RowProjector projector;
+	private SelectStatement select;
+	
+    @Override
+    public void visitInput(int i, PhoenixRel input) {
+        input.implement(this, conn);
+    }
+
+	@Override
+	public ColumnExpression newColumnExpression(int index) {
+		ColumnRef colRef = new ColumnRef(tableRef, index);
+		return colRef.newColumnExpression();
+	}
+
+
+	@Override
+	public void setContext(PhoenixConnection conn, PTable table, RexNode filter) {
+		this.conn = conn;
+		this.tableRef = new TableRef(table);
+		PhoenixStatement stmt = new PhoenixStatement(conn);
+        ColumnResolver resolver;
+		try {
+			resolver = FromCompiler.getResolver(
+			        NamedTableNode.create(
+			            null,
+			            TableName.create(tableRef.getTable().getSchemaName().getString(), tableRef.getTable().getTableName().getString()),
+			            ImmutableList.<ColumnDef>of()), conn);
+	        this.context = new StatementContext(stmt, resolver, new Scan(), new SequenceManager(stmt));
+	        // TODO: real projection
+	        this.select = SelectStatement.SELECT_STAR;
+	        this.projector = ProjectionCompiler.compile(context, select, GroupBy.EMPTY_GROUP_BY, Collections.<PDatum>emptyList());
+	        if (filter != null) {
+	        	Expression filterExpr = CalciteUtils.toExpression(filter, this);
+	        	filterExpr = WhereOptimizer.pushKeyExpressionsToScan(context, select, filterExpr);
+	        	WhereCompiler.setScanFilter(context, select, filterExpr, true, false);
+	        }
+		} catch (SQLException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Override
+	public QueryPlan makePlan() {
+		Integer limit = null;
+		OrderBy orderBy = OrderBy.EMPTY_ORDER_BY;
+		ParallelIteratorFactory iteratorFactory = null;
+        return new ScanPlan(context, select, tableRef, projector, limit, orderBy, iteratorFactory, true);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRules.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRules.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRules.java
new file mode 100644
index 0000000..77a8b7b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRules.java
@@ -0,0 +1,334 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.adapter.enumerable.EnumerableConvention;
+import org.apache.calcite.plan.*;
+import org.apache.calcite.rel.InvalidRelException;
+import org.apache.calcite.rel.RelCollationImpl;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterRule;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.util.trace.CalciteTrace;
+
+import java.util.logging.Logger;
+
+/**
+ * Rules and relational operators for
+ * {@link PhoenixRel#CONVENTION PHOENIX}
+ * calling convention.
+ */
+public class PhoenixRules {
+    private PhoenixRules() {}
+
+    protected static final Logger LOGGER = CalciteTrace.getPlannerTracer();
+
+    public static final RelOptRule[] RULES = {
+        PhoenixToEnumerableConverterRule.INSTANCE,
+        PhoenixSortRule.INSTANCE,
+        PhoenixFilterRule.INSTANCE,
+        PhoenixProjectRule.INSTANCE,
+        PhoenixAggregateRule.INSTANCE,
+        PhoenixUnionRule.INSTANCE,
+    };
+
+    /** Base class for planner rules that convert a relational expression to
+     * Phoenix calling convention. */
+    abstract static class PhoenixConverterRule extends ConverterRule {
+        protected final Convention out;
+        public PhoenixConverterRule(
+            Class<? extends RelNode> clazz,
+            RelTrait in,
+            Convention out,
+            String description) {
+            super(clazz, in, out, description);
+            this.out = out;
+        }
+    }
+
+    /**
+     * Rule to convert a {@link org.apache.calcite.rel.core.Sort} to a
+     * {@link PhoenixSort}.
+     */
+    private static class PhoenixSortRule extends PhoenixConverterRule {
+        public static final PhoenixSortRule INSTANCE = new PhoenixSortRule();
+
+        private PhoenixSortRule() {
+            super(Sort.class, Convention.NONE, PhoenixRel.CONVENTION,
+                "PhoenixSortRule");
+        }
+
+        public RelNode convert(RelNode rel) {
+            final Sort sort = (Sort) rel;
+            final RelTraitSet traitSet =
+                sort.getTraitSet().replace(out)
+                    .replace(sort.getCollation());
+            return new PhoenixSort(rel.getCluster(), traitSet,
+                convert(sort.getInput(), traitSet.replace(RelCollationImpl.EMPTY)),
+                sort.getCollation(), sort.offset, sort.fetch);
+        }
+    }
+
+    /**
+     * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalFilter} to a
+     * {@link PhoenixFilter}.
+     */
+    private static class PhoenixFilterRule extends PhoenixConverterRule {
+        private static final PhoenixFilterRule INSTANCE = new PhoenixFilterRule();
+
+        private PhoenixFilterRule() {
+            super(LogicalFilter.class, Convention.NONE, PhoenixRel.CONVENTION,
+                "PhoenixFilterRule");
+        }
+
+        public RelNode convert(RelNode rel) {
+            final LogicalFilter filter = (LogicalFilter) rel;
+            final RelTraitSet traitSet = filter.getTraitSet().replace(out);
+            return new PhoenixFilter(
+                rel.getCluster(),
+                traitSet,
+                convert(filter.getInput(), traitSet),
+                filter.getCondition());
+        }
+    }
+
+    /**
+     * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalProject}
+     * to a {@link PhoenixProject}.
+     */
+    private static class PhoenixProjectRule extends PhoenixConverterRule {
+        private static final PhoenixProjectRule INSTANCE = new PhoenixProjectRule();
+
+        private PhoenixProjectRule() {
+            super(LogicalProject.class, Convention.NONE, PhoenixRel.CONVENTION,
+                "PhoenixProjectRule");
+        }
+
+        public RelNode convert(RelNode rel) {
+            final LogicalProject project = (LogicalProject) rel;
+            final RelTraitSet traitSet = project.getTraitSet().replace(out);
+            return new PhoenixProject(project.getCluster(), traitSet,
+                convert(project.getInput(), traitSet), project.getProjects(),
+                project.getRowType());
+        }
+    }
+
+    /**
+     * Rule to convert a {@link org.apache.calcite.rel.logical.LogicalAggregate}
+     * to an {@link PhoenixAggregate}.
+     */
+    private static class PhoenixAggregateRule extends PhoenixConverterRule {
+        public static final RelOptRule INSTANCE = new PhoenixAggregateRule();
+
+        private PhoenixAggregateRule() {
+            super(LogicalAggregate.class, Convention.NONE, PhoenixRel.CONVENTION,
+                "PhoenixAggregateRule");
+        }
+
+        public RelNode convert(RelNode rel) {
+            final LogicalAggregate agg = (LogicalAggregate) rel;
+            final RelTraitSet traitSet =
+                agg.getTraitSet().replace(out);
+            try {
+                return new PhoenixAggregate(
+                    rel.getCluster(),
+                    traitSet,
+                    convert(agg.getInput(), traitSet),
+                    agg.indicator,
+                    agg.getGroupSet(),
+                    agg.getGroupSets(),
+                    agg.getAggCallList());
+            } catch (InvalidRelException e) {
+                LOGGER.warning(e.toString());
+                return null;
+            }
+        }
+    }
+
+    /**
+     * Rule to convert a {@link org.apache.calcite.rel.core.Union} to a
+     * {@link PhoenixUnion}.
+     */
+    private static class PhoenixUnionRule extends PhoenixConverterRule {
+        public static final PhoenixUnionRule INSTANCE = new PhoenixUnionRule();
+
+        private PhoenixUnionRule() {
+            super(Union.class, Convention.NONE, PhoenixRel.CONVENTION,
+                "PhoenixUnionRule");
+        }
+
+        public RelNode convert(RelNode rel) {
+            final Union union = (Union) rel;
+            final RelTraitSet traitSet = union.getTraitSet().replace(out);
+            return new PhoenixUnion(rel.getCluster(), traitSet, convertList(union.getInputs(), out),
+                union.all);
+        }
+    }
+
+    /**
+     * Rule to convert an {@link org.apache.calcite.rel.logical.LogicalIntersect}
+     * to an {@link PhoenixIntersectRel}.
+     o/
+     private static class PhoenixIntersectRule
+     extends PhoenixConverterRule {
+     private PhoenixIntersectRule(PhoenixConvention out) {
+     super(
+     LogicalIntersect.class,
+     Convention.NONE,
+     out,
+     "PhoenixIntersectRule");
+     }
+
+     public RelNode convert(RelNode rel) {
+     final LogicalIntersect intersect = (LogicalIntersect) rel;
+     if (intersect.all) {
+     return null; // INTERSECT ALL not implemented
+     }
+     final RelTraitSet traitSet =
+     intersect.getTraitSet().replace(out);
+     return new PhoenixIntersectRel(
+     rel.getCluster(),
+     traitSet,
+     convertList(intersect.getInputs(), traitSet),
+     intersect.all);
+     }
+     }
+
+     public static class PhoenixIntersectRel
+     extends Intersect
+     implements PhoenixRel {
+     public PhoenixIntersectRel(
+     RelOptCluster cluster,
+     RelTraitSet traitSet,
+     List<RelNode> inputs,
+     boolean all) {
+     super(cluster, traitSet, inputs, all);
+     assert !all;
+     }
+
+     public PhoenixIntersectRel copy(
+     RelTraitSet traitSet, List<RelNode> inputs, boolean all) {
+     return new PhoenixIntersectRel(getCluster(), traitSet, inputs, all);
+     }
+
+     public SqlString implement(PhoenixImplementor implementor) {
+     return setOpSql(this, implementor, " intersect ");
+     }
+     }
+
+     /**
+     * Rule to convert an {@link org.apache.calcite.rel.logical.LogicalMinus}
+     * to an {@link PhoenixMinusRel}.
+     o/
+     private static class PhoenixMinusRule
+     extends PhoenixConverterRule {
+     private PhoenixMinusRule(PhoenixConvention out) {
+     super(
+     LogicalMinus.class,
+     Convention.NONE,
+     out,
+     "PhoenixMinusRule");
+     }
+
+     public RelNode convert(RelNode rel) {
+     final LogicalMinus minus = (LogicalMinus) rel;
+     if (minus.all) {
+     return null; // EXCEPT ALL not implemented
+     }
+     final RelTraitSet traitSet =
+     rel.getTraitSet().replace(out);
+     return new PhoenixMinusRel(
+     rel.getCluster(),
+     traitSet,
+     convertList(minus.getInputs(), traitSet),
+     minus.all);
+     }
+     }
+
+     public static class PhoenixMinusRel
+     extends Minus
+     implements PhoenixRel {
+     public PhoenixMinusRel(
+     RelOptCluster cluster,
+     RelTraitSet traitSet,
+     List<RelNode> inputs,
+     boolean all) {
+     super(cluster, traitSet, inputs, all);
+     assert !all;
+     }
+
+     public PhoenixMinusRel copy(
+     RelTraitSet traitSet, List<RelNode> inputs, boolean all) {
+     return new PhoenixMinusRel(getCluster(), traitSet, inputs, all);
+     }
+
+     public SqlString implement(PhoenixImplementor implementor) {
+     return setOpSql(this, implementor, " minus ");
+     }
+     }
+     */
+
+  /*
+  public static class PhoenixValuesRule extends PhoenixConverterRule {
+    private PhoenixValuesRule() {
+      super(Values.class, Convention.NONE, PhoenixRel.CONVENTION, "PhoenixValuesRule");
+    }
+
+    @Override public RelNode convert(RelNode rel) {
+      Values valuesRel = (Values) rel;
+      return new PhoenixValuesRel(
+          valuesRel.getCluster(),
+          valuesRel.getRowType(),
+          valuesRel.getTuples(),
+          valuesRel.getTraitSet().plus(out));
+    }
+  }
+
+  public static class PhoenixValuesRel
+      extends Values
+      implements PhoenixRel {
+    PhoenixValuesRel(
+        RelOptCluster cluster,
+        RelDataType rowType,
+        List<List<RexLiteral>> tuples,
+        RelTraitSet traitSet) {
+      super(cluster, rowType, tuples, traitSet);
+    }
+
+    @Override public RelNode copy(
+        RelTraitSet traitSet, List<RelNode> inputs) {
+      assert inputs.isEmpty();
+      return new PhoenixValuesRel(
+          getCluster(), rowType, tuples, traitSet);
+    }
+
+    public SqlString implement(PhoenixImplementor implementor) {
+      throw new AssertionError(); // TODO:
+    }
+  }
+*/
+
+    /**
+     * Rule to convert a relational expression from
+     * {@link org.apache.phoenix.calcite.PhoenixRel#CONVENTION} to
+     * {@link org.apache.calcite.adapter.enumerable.EnumerableConvention}.
+     */
+    public static class PhoenixToEnumerableConverterRule extends ConverterRule {
+        public static final ConverterRule INSTANCE =
+            new PhoenixToEnumerableConverterRule();
+
+        private PhoenixToEnumerableConverterRule() {
+            super(RelNode.class, PhoenixRel.CONVENTION, EnumerableConvention.INSTANCE,
+                "PhoenixToEnumerableConverterRule");
+        }
+
+        @Override public RelNode convert(RelNode rel) {
+            RelTraitSet newTraitSet = rel.getTraitSet().replace(getOutConvention());
+            return new PhoenixToEnumerableConverter(rel.getCluster(), newTraitSet, rel);
+        }
+    }
+}
+
+// End PhoenixRules.java

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSchema.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSchema.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSchema.java
new file mode 100644
index 0000000..d48ffca
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSchema.java
@@ -0,0 +1,135 @@
+package org.apache.phoenix.calcite;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.schema.*;
+import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.compile.FromCompiler;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.parse.ColumnDef;
+import org.apache.phoenix.parse.NamedTableNode;
+import org.apache.phoenix.parse.TableName;
+import org.apache.phoenix.schema.MetaDataClient;
+import org.apache.phoenix.schema.TableRef;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * Implementation of Calcite's {@link Schema} SPI for Phoenix.
+ */
+public class PhoenixSchema implements Schema {
+    private final String schemaName = null;
+    private final PhoenixConnection pc;
+    protected final MetaDataClient client;
+
+    PhoenixSchema(PhoenixConnection pc) {
+        this.pc = pc;
+        this.client = new MetaDataClient(pc);
+    }
+
+    private static Schema create(SchemaPlus parentSchema, String name, Map<String, Object> operand) {
+        String url = (String) operand.get("url");
+        final Properties properties = new Properties();
+        for (Map.Entry<String, Object> entry : operand.entrySet()) {
+            properties.setProperty(entry.getKey(), String.valueOf(entry.getValue()));
+        }
+        try {
+            final Connection connection =
+                DriverManager.getConnection(url, properties);
+            final PhoenixConnection phoenixConnection =
+                connection.unwrap(PhoenixConnection.class);
+            return new PhoenixSchema(phoenixConnection);
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Table getTable(String name) {
+        try {
+            ColumnResolver x = FromCompiler.getResolver(
+                NamedTableNode.create(
+                    null,
+                    TableName.create(schemaName, name),
+                    ImmutableList.<ColumnDef>of()), pc);
+            final List<TableRef> tables = x.getTables();
+            assert tables.size() == 1;
+            return new PhoenixTable(pc, tables.get(0).getTable());
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public Set<String> getTableNames() {
+        return ImmutableSet.of("ATABLE");
+    }
+
+    @Override
+    public Collection<Function> getFunctions(String name) {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Set<String> getFunctionNames() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Schema getSubSchema(String name) {
+        return null;
+    }
+
+    @Override
+    public Set<String> getSubSchemaNames() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public Expression getExpression(SchemaPlus parentSchema, String name) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isMutable() {
+        return false;
+    }
+
+    @Override
+    public boolean contentsHaveChangedSince(long lastCheck, long now) {
+        return false;
+    }
+
+    /** Schema factory that creates a
+     * {@link org.apache.phoenix.calcite.PhoenixSchema}.
+     * This allows you to create a Phoenix schema inside a model.json file.
+     *
+     * <pre>{@code
+     * {
+     *   version: '1.0',
+     *   defaultSchema: 'HR',
+     *   schemas: [
+     *     {
+     *       name: 'HR',
+     *       type: 'custom',
+     *       factory: 'org.apache.phoenix.calcite.PhoenixSchema.Factory',
+     *       operand: {
+     *         url: "jdbc:phoenix:localhost",
+     *         user: "scott",
+     *         password: "tiger"
+     *       }
+     *     }
+     *   ]
+     * }
+     * }</pre>
+     */
+    public static class Factory implements SchemaFactory {
+        public Schema create(SchemaPlus parentSchema, String name, Map<String, Object> operand) {
+            return PhoenixSchema.create(parentSchema, name, operand);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
new file mode 100644
index 0000000..8062c1b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
@@ -0,0 +1,32 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Sort}
+ * relational expression in Phoenix.
+ *
+ * <p>Like {@code Sort}, it also supports LIMIT and OFFSET.
+ */
+public class PhoenixSort extends Sort implements PhoenixRel {
+    public PhoenixSort(RelOptCluster cluster, RelTraitSet traits, RelNode child, RelCollation collation, RexNode offset, RexNode fetch) {
+        super(cluster, traits, child, collation, offset, fetch);
+    }
+
+    @Override
+    public PhoenixSort copy(RelTraitSet traitSet, RelNode newInput, RelCollation newCollation, RexNode offset, RexNode fetch) {
+        return new PhoenixSort(getCluster(), traitSet, newInput, newCollation, offset, fetch);
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        implementor.visitInput(0, (PhoenixRel) getInput());
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
new file mode 100644
index 0000000..9b58d68
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
@@ -0,0 +1,77 @@
+package org.apache.phoenix.calcite;
+
+import com.google.common.base.Preconditions;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptTable;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.schema.Statistic;
+import org.apache.calcite.schema.TranslatableTable;
+import org.apache.calcite.schema.impl.AbstractTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PTable;
+
+/**
+ * Implementation of Calcite {@link org.apache.calcite.schema.Table} SPI for
+ * Phoenix.
+ */
+public class PhoenixTable extends AbstractTable implements TranslatableTable {
+  public final PTable pTable;
+  public final PhoenixConnection pc;
+
+  public PhoenixTable(PhoenixConnection pc, PTable pTable) {
+      this.pc = Preconditions.checkNotNull(pc);
+      this.pTable = Preconditions.checkNotNull(pTable);
+    }
+    
+    public PTable getTable() {
+    	return pTable;
+    }
+
+    @Override
+    public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        final RelDataTypeFactory.FieldInfoBuilder builder = typeFactory.builder();
+        for (PColumn pColumn : pTable.getColumns()) {
+            final int sqlTypeId = pColumn.getDataType().getResultSetSqlType();
+            final PDataType pDataType = PDataType.fromTypeId(sqlTypeId);
+            final SqlTypeName sqlTypeName1 = SqlTypeName.valueOf(pDataType.getSqlTypeName());
+            final Integer maxLength = pColumn.getMaxLength();
+            final Integer scale = pColumn.getScale();
+            if (maxLength != null && scale != null) {
+                builder.add(pColumn.getName().getString(), sqlTypeName1, maxLength, scale);
+            } else if (maxLength != null) {
+                builder.add(pColumn.getName().getString(), sqlTypeName1, maxLength);
+            } else {
+                builder.add(pColumn.getName().getString(), sqlTypeName1);
+            }
+        }
+        return builder.build();
+    }
+
+    @Override
+    public RelNode toRel(RelOptTable.ToRelContext context, RelOptTable relOptTable) {
+        final RelOptCluster cluster = context.getCluster();
+        return new PhoenixTableScan(cluster, cluster.traitSetOf(PhoenixRel.CONVENTION), relOptTable, null);
+    }
+
+    @Override
+    public Statistic getStatistic() {
+        return new Statistic() {
+            @Override
+            public Double getRowCount() {
+                return 100d;
+            }
+
+            @Override
+            public boolean isKey(ImmutableBitSet immutableBitSet) {
+                return false;
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
new file mode 100644
index 0000000..a08ab73
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
@@ -0,0 +1,60 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.plan.*;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+
+import java.util.List;
+
+/**
+ * Scan of a Phoenix table.
+ */
+public class PhoenixTableScan extends TableScan implements PhoenixRel {
+    public final RexNode filter;
+
+    protected PhoenixTableScan(RelOptCluster cluster, RelTraitSet traits, RelOptTable table, RexNode filter) {
+        super(cluster, traits, table);
+        this.filter = filter;
+    }
+
+    @Override
+    public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        assert inputs.isEmpty();
+        return this;
+    }
+
+    @Override
+    public void register(RelOptPlanner planner) {
+        RelOptRule[] rules = PhoenixRules.RULES;
+        for (RelOptRule rule : rules) {
+            planner.addRule(rule);
+        }
+        planner.addRule(PhoenixFilterScanMergeRule.INSTANCE);
+    }
+
+    @Override
+    public RelWriter explainTerms(RelWriter pw) {
+        return super.explainTerms(pw)
+            .itemIf("filter", filter, filter != null);
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        RelOptCost cost = super.computeSelfCost(planner).multiplyBy(PHOENIX_FACTOR);
+        if (filter != null && !filter.isAlwaysTrue()) {
+            final Double selectivity = RelMetadataQuery.getSelectivity(this, filter);
+            cost = cost.multiplyBy(selectivity);
+        }
+        return cost;
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        final PhoenixTable phoenixTable = table.unwrap(PhoenixTable.class);
+        implementor.setContext(phoenixTable.pc, phoenixTable.getTable(), filter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
new file mode 100644
index 0000000..fc2af9c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
@@ -0,0 +1,72 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.enumerable.*;
+import org.apache.calcite.linq4j.tree.*;
+import org.apache.calcite.plan.*;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.convert.ConverterImpl;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.phoenix.compile.QueryPlan;
+
+import java.util.List;
+
+/**
+ * Scan of a Phoenix table.
+ */
+public class PhoenixToEnumerableConverter extends ConverterImpl implements EnumerableRel {
+    protected PhoenixToEnumerableConverter(
+        RelOptCluster cluster,
+        RelTraitSet traits,
+        RelNode input) {
+        super(cluster, ConventionTraitDef.INSTANCE, traits, input);
+    }
+
+    @Override public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        return new PhoenixToEnumerableConverter(getCluster(), traitSet, sole(inputs));
+    }
+
+    @Override public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.1);
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        // Generates code that instantiates a result iterator, then converts it
+        // to an enumerable.
+        //
+        //   ResultIterator iterator = root.get("x");
+        //   return CalciteRuntime.toEnumerable(iterator);
+        final BlockBuilder list = new BlockBuilder();
+        QueryPlan plan = makePlan((PhoenixRel)getInput());
+        Expression var = stash(implementor, plan, QueryPlan.class);
+        final RelDataType rowType = getRowType();
+        final PhysType physType =
+            PhysTypeImpl.of(
+                implementor.getTypeFactory(), rowType,
+                pref.prefer(JavaRowFormat.ARRAY));
+        final Expression iterator_ =
+            list.append("iterator", var);
+        final Expression enumerable_ =
+            list.append("enumerable",
+                Expressions.call(BuiltInMethod.TO_ENUMERABLE.method,
+                    iterator_));
+        list.add(Expressions.return_(null, enumerable_));
+        return implementor.result(physType, list.toBlock());
+    }
+    
+    static QueryPlan makePlan(PhoenixRel rel) {
+        final PhoenixRel.Implementor phoenixImplementor = new PhoenixRelImplementorImpl();
+        phoenixImplementor.visitInput(0, rel);
+        return phoenixImplementor.makePlan();
+    }
+
+    static Expression stash(EnumerableRelImplementor implementor, Object o, Class clazz) {
+        ParameterExpression x = implementor.register(o, clazz);
+        MethodCallExpression e =
+            Expressions.call(implementor.getRootExpression(),
+                org.apache.calcite.util.BuiltInMethod.DATA_CONTEXT_GET.method,
+                Expressions.constant(x.name));
+        return Expressions.convert_(e, clazz);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
new file mode 100644
index 0000000..85493fb
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
@@ -0,0 +1,39 @@
+package org.apache.phoenix.calcite;
+
+import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.SetOp;
+import org.apache.calcite.rel.core.Union;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Union}
+ * relational expression in Phoenix.
+ */
+public class PhoenixUnion extends Union implements PhoenixRel {
+    protected PhoenixUnion(RelOptCluster cluster, RelTraitSet traits, List<RelNode> inputs, boolean all) {
+        super(cluster, traits, inputs, all);
+        assert getConvention() == PhoenixRel.CONVENTION;
+
+        for (RelNode input : inputs) {
+            assert getConvention() == input.getConvention();
+        }
+    }
+
+    @Override
+    public PhoenixUnion copy(RelTraitSet traits, List<RelNode> inputs, boolean all) {
+        return new PhoenixUnion(getCluster(), traits, inputs, all);
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        for (Ord<RelNode> input : Ord.zip(inputs)) {
+            implementor.visitInput(input.i, (PhoenixRel) input.e);
+        }
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
new file mode 100644
index 0000000..420152b
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
@@ -0,0 +1,36 @@
+package org.apache.phoenix.calcite;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+
+import java.util.List;
+
+/**
+ * Implementation of {@link org.apache.calcite.rel.core.Values}
+ * relational expression in Phoenix.
+ */
+public class PhoenixValues extends Values implements PhoenixRel {
+    public PhoenixValues(RelOptCluster cluster, RelDataType rowType, ImmutableList<ImmutableList<RexLiteral>> tuples, RelTraitSet traits) {
+        super(cluster, rowType, tuples, traits);
+        assert getConvention() == PhoenixRel.CONVENTION;
+    }
+
+    @Override
+    public PhoenixValues copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        assert traitSet.containsIfApplicable(Convention.NONE);
+        assert inputs.isEmpty();
+        return new PhoenixValues(getCluster(), rowType, tuples, traitSet);
+    }
+
+    @Override
+    public void implement(Implementor implementor, PhoenixConnection conn) {
+        throw new UnsupportedOperationException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/76a858a4/phoenix-core/src/test/java/org/apache/phoenix/calcite/ToExpressionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/calcite/ToExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/calcite/ToExpressionTest.java
new file mode 100644
index 0000000..91628ff
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/calcite/ToExpressionTest.java
@@ -0,0 +1,77 @@
+package org.apache.phoenix.calcite;
+
+import static org.junit.Assert.*;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.phoenix.calcite.PhoenixRel.Implementor;
+import org.apache.phoenix.compile.ColumnResolver;
+import org.apache.phoenix.compile.FromCompiler;
+import org.apache.phoenix.compile.GroupByCompiler;
+import org.apache.phoenix.compile.HavingCompiler;
+import org.apache.phoenix.compile.LimitCompiler;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.compile.WhereCompiler;
+import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
+import org.apache.phoenix.expression.ColumnExpression;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.parse.ParseNode;
+import org.apache.phoenix.parse.SQLParser;
+import org.apache.phoenix.parse.SelectStatement;
+import org.apache.phoenix.parse.SubqueryParseNode;
+import org.apache.phoenix.query.BaseConnectionlessQueryTest;
+import org.apache.phoenix.schema.ColumnRef;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableKey;
+import org.junit.Test;
+
+
+public class ToExpressionTest extends BaseConnectionlessQueryTest {
+	
+	private static Expression compileExpression(PhoenixStatement statement, StatementContext context, String selectStmt) throws SQLException {
+		// Re-parse the WHERE clause as we don't store it any where
+        SelectStatement select = new SQLParser(selectStmt).parseQuery();
+        Expression where = WhereCompiler.compile(context, select, null, Collections.<SubqueryParseNode>emptySet());
+        return where;
+	}
+	
+	@Test
+	public void toExpressionTest() throws Exception {
+		final String expectedColName = "K2";
+		final Object expectedValue = "foo";
+		Connection conn = DriverManager.getConnection(getUrl());
+		conn.createStatement().execute("CREATE TABLE t(k1 VARCHAR PRIMARY KEY, k2 VARCHAR, v1 VARCHAR)");
+		final PTable table = conn.unwrap(PhoenixConnection.class).getMetaDataCache().getTable(new PTableKey(null,"T"));
+		PhoenixStatement stmt = conn.createStatement().unwrap(PhoenixStatement.class);
+		String query = "SELECT * FROM T WHERE K2 = 'foo'";
+		QueryPlan plan = stmt.compileQuery(query);
+		Expression where = compileExpression(stmt, plan.getContext(), query);
+		
+		JavaTypeFactoryImpl typeFactory = new JavaTypeFactoryImpl();
+		RexBuilder builder = new RexBuilder(typeFactory);
+		RelDataType dataType = typeFactory.createSqlType(SqlTypeName.VARCHAR, 10);
+		RexInputRef ref = builder.makeInputRef(dataType,table.getColumn(expectedColName).getPosition());
+		RexNode lit = builder.makeLiteral(expectedValue, dataType, true);
+		RexNode call = builder.makeCall(SqlStdOperatorTable.EQUALS, ref, lit);
+		
+		Implementor implementor = new PhoenixRelImplementorImpl();
+		implementor.setContext(conn.unwrap(PhoenixConnection.class), table, null);
+		Expression e = CalciteUtils.toExpression(call, implementor);
+		assertEquals(where,e);
+	}
+}


Mime
View raw message