You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ma...@apache.org on 2016/03/31 04:37:13 UTC

phoenix git commit: PHOENIX-2781 Support multi-tenant secondary index in Calcite-Phoenix; Fix some resource leak in PhoenixCalciteConnection; Fix PhoenixTableScan costing to cope with CALCITE-1165; Refine test framework and fix local index testing.

Repository: phoenix
Updated Branches:
  refs/heads/calcite 6ec421c12 -> caf1c6ed9


PHOENIX-2781 Support multi-tenant secondary index in Calcite-Phoenix; Fix some resource leak in PhoenixCalciteConnection; Fix PhoenixTableScan costing to cope with CALCITE-1165; Refine test framework and fix local index testing.


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

Branch: refs/heads/calcite
Commit: caf1c6ed94e4a26e60bbe1ddbaadaae3adebca66
Parents: 6ec421c
Author: maryannxue <ma...@gmail.com>
Authored: Wed Mar 30 22:37:02 2016 -0400
Committer: maryannxue <ma...@gmail.com>
Committed: Wed Mar 30 22:37:02 2016 -0400

----------------------------------------------------------------------
 .../apache/phoenix/calcite/BaseCalciteIT.java   |  66 +++-
 .../phoenix/calcite/BaseCalciteIndexIT.java     |  63 ++++
 .../phoenix/calcite/CalciteGlobalIndexIT.java   | 259 ++++++++++++++++
 .../apache/phoenix/calcite/CalciteIndexIT.java  | 306 -------------------
 .../phoenix/calcite/CalciteLocalIndexIT.java    | 277 +++++++++++++++++
 .../calcite/jdbc/PhoenixCalciteFactory.java     |  12 +
 .../apache/phoenix/calcite/PhoenixTable.java    |   9 +-
 .../calcite/rel/PhoenixAbstractAggregate.java   |  10 +-
 .../phoenix/calcite/rel/PhoenixTableScan.java   |   3 +-
 .../rel/PhoenixToEnumerableConverter.java       |   5 +-
 10 files changed, 681 insertions(+), 329 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIT.java
index 65a9c6e..607a93f 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIT.java
@@ -46,6 +46,7 @@ import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.schema.TableAlreadyExistsException;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.junit.BeforeClass;
 
@@ -58,6 +59,7 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
         Map<String,String> props = getDefaultProps();
         props.put(QueryServices.RUN_UPDATE_STATS_ASYNC, Boolean.FALSE.toString());
         props.put(QueryServices.STATS_GUIDEPOST_WIDTH_BYTES_ATTRIB, Long.toString(1000));
+        props.put(QueryServices.THREAD_POOL_SIZE_ATTRIB, Integer.toString(200));
         setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
     }
     
@@ -68,6 +70,20 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
     public static Start start(Properties props) {
         return new Start(props);
     }
+    
+    public static Start startPhoenixStandalone(Properties props) {
+        return new Start(props) {
+            Connection createConnection() throws Exception {
+                return DriverManager.getConnection(
+                        getUrl(), 
+                        props);
+            }
+            
+            String getExplainPlanString() {
+                return "explain";
+            }
+        };
+    }
 
     public static class Start {
         protected final Properties props;
@@ -83,6 +99,10 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
                             + getUrl().substring(PhoenixRuntime.JDBC_PROTOCOL.length() + 1), 
                     props);
         }
+        
+        String getExplainPlanString() {
+            return "explain plan for";
+        }
 
         public Sql sql(String sql) {
             return new Sql(this, sql);
@@ -120,12 +140,6 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
             this.sql = sql;
         }
 
-        public static List<Object[]> getResult(ResultSet resultSet) throws SQLException {
-            final List<Object[]> list = Lists.newArrayList();
-            populateResult(resultSet, list);
-            return list;
-        }
-
         private static void populateResult(ResultSet resultSet, List<Object[]> list) throws SQLException {
             final int columnCount = resultSet.getMetaData().getColumnCount();
             while (resultSet.next()) {
@@ -138,11 +152,11 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
         }
 
         public Sql explainIs(String expected) throws SQLException {
-            final List<Object[]> list = getResult("explain plan for " + sql);
-            if (list.size() != 1) {
-                fail("explain should return 1 row, got " + list.size());
-            }
-            String explain = (String) (list.get(0)[0]);
+            final Statement statement = start.getConnection().createStatement();
+            final ResultSet resultSet = statement.executeQuery(start.getExplainPlanString() + " " + sql);
+            String explain = QueryUtil.getExplainPlan(resultSet);
+            resultSet.close();
+            statement.close();
             assertEquals(explain, expected);
             return this;
         }
@@ -155,10 +169,11 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
             return execute;
         }
 
-        public List<Object[]> getResult(String sql) throws SQLException {
+        public List<Object[]> getResult() throws SQLException {
             final Statement statement = start.getConnection().createStatement();
             final ResultSet resultSet = statement.executeQuery(sql);
-            List<Object[]> list = getResult(resultSet);
+            final List<Object[]> list = Lists.newArrayList();
+            populateResult(resultSet, list);
             resultSet.close();
             statement.close();
             return list;
@@ -195,6 +210,20 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
             return this;
         }
         
+        public Sql sameResultAsPhoenixStandalone() throws SQLException {
+            Start phoenixStart = startPhoenixStandalone(this.start.props);
+            List<Object[]> result = phoenixStart.sql(this.sql).getResult();
+            phoenixStart.close();
+            return resultIs(result.toArray(new Object[result.size()][]));
+        }
+        
+        public Sql sameResultAsPhoenixStandalone(int orderedCount) throws SQLException {
+            Start phoenixStart = startPhoenixStandalone(this.start.props);
+            List<Object[]> result = phoenixStart.sql(this.sql).getResult();
+            phoenixStart.close();
+            return resultIs(orderedCount, result.toArray(new Object[result.size()][]));
+        }
+        
         private void checkResultOrdered(ResultSet resultSet, Object[][] expected) throws SQLException {
             int expectedCount = expected.length;
             int count = 0;
@@ -225,6 +254,9 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
                 expectedResults.add(row);
             }
             while (resultSet.next()) {
+                if (actualResults.size() >= expected.length) {
+                    fail("Got more rows than expected after getting results: " + actualResults);
+                }
                 // check the ordered part
                 Object[] row = expected[actualResults.size()];
                 for (int i = 0; i < orderedCount; i++) {
@@ -403,7 +435,9 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
             conn.commit();
             
             if (index != null) {
-                conn.createStatement().execute("CREATE " + index + " " + NOSALT_TABLE_SALTED_INDEX_NAME + " ON " + NOSALT_TABLE_NAME + " (col0) SALT_BUCKETS=4");
+                conn.createStatement().execute(
+                        "CREATE " + index + " " + NOSALT_TABLE_SALTED_INDEX_NAME + " ON " + NOSALT_TABLE_NAME + " (col0)"
+                        + (index.toUpperCase().startsWith("LOCAL") ? "" : " SALT_BUCKETS=4"));
                 conn.commit();
             }
             
@@ -423,7 +457,9 @@ public class BaseCalciteIT extends BaseClientManagedTimeIT {
             
             if (index != null) {
                 conn.createStatement().execute("CREATE " + index + " " + SALTED_TABLE_NOSALT_INDEX_NAME + " ON " + SALTED_TABLE_NAME + " (col0)");
-                conn.createStatement().execute("CREATE " + index + " " + SALTED_TABLE_SALTED_INDEX_NAME + " ON " + SALTED_TABLE_NAME + " (col1) INCLUDE (col0) SALT_BUCKETS=4");
+                conn.createStatement().execute(
+                        "CREATE " + index + " " + SALTED_TABLE_SALTED_INDEX_NAME + " ON " + SALTED_TABLE_NAME + " (col1) INCLUDE (col0)"
+                        + (index.toUpperCase().startsWith("LOCAL") ? "" : " SALT_BUCKETS=4"));
                 conn.commit();
             }
         } catch (TableAlreadyExistsException e) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIndexIT.java
new file mode 100644
index 0000000..62943aa
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/BaseCalciteIndexIT.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 org.apache.phoenix.calcite;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.Properties;
+
+import org.junit.Before;
+
+public class BaseCalciteIndexIT extends BaseCalciteIT {
+    
+    private final boolean localIndex;
+    
+    public BaseCalciteIndexIT(boolean localIndex) {
+        this.localIndex = localIndex;
+    }
+    
+    @Before
+    public void initTable() throws Exception {
+        final String url = getUrl();
+        final String index = localIndex ? "LOCAL INDEX" : "INDEX";
+        initATableValues(getOrganizationId(), null, url);
+        initSaltedTables(index);
+        initMultiTenantTables(index);
+        Connection connection = DriverManager.getConnection(url);
+        connection.createStatement().execute("CREATE " + index + " IF NOT EXISTS IDX1 ON aTable (a_string) INCLUDE (b_string, x_integer)");
+        connection.createStatement().execute("CREATE " + index + " IF NOT EXISTS IDX2 ON aTable (b_string) INCLUDE (a_string, y_integer)");
+        connection.createStatement().execute("CREATE " + index + " IF NOT EXISTS IDX_FULL ON aTable (b_string) INCLUDE (a_string, a_integer, a_date, a_time, a_timestamp, x_decimal, x_long, x_integer, y_integer, a_byte, a_short, a_float, a_double, a_unsigned_float, a_unsigned_double)");
+        connection.createStatement().execute("UPDATE STATISTICS ATABLE");
+        connection.createStatement().execute("UPDATE STATISTICS " + NOSALT_TABLE_NAME);
+        connection.createStatement().execute("UPDATE STATISTICS " + SALTED_TABLE_NAME);
+        connection.createStatement().execute("UPDATE STATISTICS " + MULTI_TENANT_TABLE);
+        connection.close();
+        
+        Properties props = new Properties();
+        props.setProperty("TenantId", "10");
+        connection = DriverManager.getConnection(url, props);
+        connection.createStatement().execute("UPDATE STATISTICS " + MULTI_TENANT_VIEW1);
+        connection.close();
+        
+        props.setProperty("TenantId", "20");
+        connection = DriverManager.getConnection(url, props);
+        connection.createStatement().execute("UPDATE STATISTICS " + MULTI_TENANT_VIEW2);
+        connection.close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
new file mode 100644
index 0000000..090f47f
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.calcite;
+
+import static org.junit.Assert.fail;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.junit.Test;
+
+public class CalciteGlobalIndexIT extends BaseCalciteIndexIT {
+    
+    public CalciteGlobalIndexIT() {
+        super(false);
+    }
+    
+    @Test public void testIndex() throws Exception {
+        start(true, 1000f).sql("select * from aTable where b_string = 'b'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(ORGANIZATION_ID=[$1], ENTITY_ID=[$2], A_STRING=[$3], B_STRING=[$0], A_INTEGER=[$4], A_DATE=[$5], A_TIME=[$6], A_TIMESTAMP=[$7], X_DECIMAL=[$8], X_LONG=[$9], X_INTEGER=[$10], Y_INTEGER=[$11], A_BYTE=[$12], A_SHORT=[$13], A_FLOAT=[$14], A_DOUBLE=[$15], A_UNSIGNED_FLOAT=[$16], A_UNSIGNED_DOUBLE=[$17])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
+            .close();
+        start(true, 1000f).sql("select x_integer from aTable")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(X_INTEGER=[$4])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]])\n")
+            .close();
+        start(true, 1000f).sql("select a_string from aTable order by a_string")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$0])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
+            .close();
+        start(true, 1000000f).sql("select a_string from aTable order by organization_id")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$2], ORGANIZATION_ID=[$0])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, ATABLE]], scanOrder=[FORWARD])\n")
+            .close();
+        start(true, 1000f).sql("select a_integer from aTable order by a_string")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerSort(sort0=[$1], dir0=[ASC])\n" +
+                       "    PhoenixServerProject(A_INTEGER=[$4], A_STRING=[$3])\n" +
+                       "      PhoenixTableScan(table=[[phoenix, IDX_FULL]])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, b_string from aTable where a_string = 'a'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$0], B_STRING=[$3])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]], filter=[=($0, 'a')])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, b_string from aTable where b_string = 'b'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$3], B_STRING=[$0])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX2]], filter=[=($0, 'b')])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, b_string, x_integer, y_integer from aTable where b_string = 'b'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$3], B_STRING=[$0], X_INTEGER=[$10], Y_INTEGER=[$11])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, count(*) from aTable group by a_string")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerAggregate(group=[{0}], EXPR$1=[COUNT()], isOrdered=[true])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
+            .close();
+    }
+    
+    @Test public void testSaltedIndex() throws Exception {
+        start(true, 1f).sql("select count(*) from " + NOSALT_TABLE_NAME + " where col0 > 3")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_NOSALT_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 3)])\n")
+                .resultIs(0, new Object[][]{{999L}})
+                .close();
+        start(true, 1f).sql("select mypk0, mypk1, col0 from " + NOSALT_TABLE_NAME + " where col0 <= 4")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_NOSALT_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 4)])\n")
+                .resultIs(0, new Object[][] {
+                        {2, 3, 4},
+                        {1, 2, 3}})
+                .close();
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " where mypk0 < 3")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[<($0, 3)])\n")
+                .resultIs(0, new Object[][] {
+                        {1, 2, 3, 4},
+                        {2, 3, 4, 5}})
+                .close();
+        start(true, 1f).sql("select count(*) from " + SALTED_TABLE_NAME + " where col0 > 3")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_SALTED_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 3)])\n")
+                .resultIs(0, new Object[][]{{999L}})
+                .close();
+        start(true, 1f).sql("select mypk0, mypk1, col0 from " + SALTED_TABLE_NAME + " where col0 <= 4")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_SALTED_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 4)])\n")
+                .resultIs(0, new Object[][] {
+                        {2, 3, 4},
+                        {1, 2, 3}})
+                .close();
+        start(true, 1f).sql("select count(*) from " + SALTED_TABLE_NAME + " where col1 > 4")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 4)])\n")
+                .resultIs(0, new Object[][]{{999L}})
+                .close();
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " where col1 <= 5 order by col1")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 5)], scanOrder=[FORWARD])\n")
+                .resultIs(new Object[][] {
+                        {1, 2, 3, 4},
+                        {2, 3, 4, 5}})
+                .close();
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk0 = s2.mypk0 and s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerJoin(condition=[AND(=($0, $4), =($1, $5))], joinType=[inner])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n" +
+                           "    PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n")
+                .resultIs(0, new Object[][] {
+                        {501, 502, 503, 504, 501, 502, 503, 504}})
+                .close();
+    }
+    
+    @Test public void testMultiTenant() throws Exception {
+        Properties props = getConnectionProps(true, 1f);
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where tenant_id = '10' and id <= '0004'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[AND(=(CAST($0):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '10'), <=($1, '0004'))])\n")
+                .resultIs(0, new Object[][] {
+                        {"10", "0002", 3, 4, 5},
+                        {"10", "0003", 4, 5, 6},
+                        {"10", "0004", 5, 6, 7}})
+                .close();
+        
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where tenant_id = '20' and col1 < 8")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(TENANT_ID=[$0], ID=[$2], COL0=[$3], COL1=[CAST($1):INTEGER], COL2=[$4])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[AND(=(CAST($0):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '20'), <(CAST($1):INTEGER, 8))])\n")
+                .resultIs(0, new Object[][] {
+                        {"20", "0004", 5, 6, 7},
+                        {"20", "0005", 6, 7, 8}})
+                .close();
+        
+        try {
+            start(props).sql("select * from " + MULTI_TENANT_VIEW1)
+                .explainIs("")
+                .close();
+            fail("Should have got SQLException.");
+        } catch (SQLException e) {
+        }
+        
+        props.setProperty("TenantId", "15");
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where id = '0284'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[=(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0284')])\n")
+                .resultIs(0, new Object[][] {
+                        {"0284", 285, 286, 287}})
+                .close();
+        
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where col1 > 1000")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[$2], COL1=[CAST($0):INTEGER], COL2=[$3])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 1000)])\n")
+                .resultIs(0, new Object[][] {
+                        {"0999", 1000, 1001, 1002},
+                        {"1000", 1001, 1002, 1003},
+                        {"1001", 1002, 1003, 1004},
+                        {"1002", 1003, 1004, 1005}})
+                .close();
+        
+        try {
+            start(props).sql("select * from " + MULTI_TENANT_VIEW1)
+                .explainIs("")
+                .close();
+            fail("Should have got SQLException.");
+        } catch (SQLException e) {
+        }
+
+        props.setProperty("TenantId", "10");
+        start(props).sql("select * from " + MULTI_TENANT_VIEW1 + " where id = '0512'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[=(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0512')])\n")
+                .resultIs(0, new Object[][] {
+                        {"0512", 513, 514, 515}})
+                .close();
+        
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where col1 <= 6")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[$2], COL1=[CAST($0):INTEGER], COL2=[$3])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 6)])\n")
+                .resultIs(0, new Object[][] {
+                        {"0002", 3, 4, 5},
+                        {"0003", 4, 5, 6},
+                        {"0004", 5, 6, 7}})
+                .close();
+        
+        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW1 + " where col0 >= 1000")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, S1, IDX_MULTITENANT_TEST_VIEW1]], filter=[>=(CAST($0):INTEGER, 1000)])\n")
+                .resultIs(0, new Object[][] {
+                        {"0999", 1000},
+                        {"1000", 1001},
+                        {"1001", 1002}})
+                .close();
+
+        props.setProperty("TenantId", "20");
+        start(props).sql("select * from " + MULTI_TENANT_VIEW2 + " where id = '0765'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[AND(>($3, 7), =(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0765'))])\n")
+                .resultIs(0, new Object[][] {
+                        {"0765", 766, 767, 768}})
+                .close();
+        
+        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW2 + " where col0 between 272 and 275")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, S2, IDX_MULTITENANT_TEST_VIEW2]], filter=[AND(>=(CAST($0):INTEGER, 272), <=(CAST($0):INTEGER, 275))])\n")
+                .resultIs(0, new Object[][] {
+                        {"0271", 272},
+                        {"0272", 273},
+                        {"0273", 274},
+                        {"0274", 275}})
+                .close();
+        
+        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW2 + " order by col0 limit 5")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixLimit(fetch=[5])\n" +
+                           "    PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, S2, IDX_MULTITENANT_TEST_VIEW2]], scanOrder=[FORWARD])\n")
+                .resultIs(new Object[][] {
+                        {"0005", 6},
+                        {"0006", 7},
+                        {"0007", 8},
+                        {"0008", 9},
+                        {"0009", 10}})
+                .close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIndexIT.java
deleted file mode 100644
index 067d0fc..0000000
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIndexIT.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.phoenix.calcite;
-
-import static org.junit.Assert.fail;
-
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Properties;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class CalciteIndexIT extends BaseCalciteIT {
-    
-    private final boolean localIndex;
-    
-    public CalciteIndexIT(boolean localIndex) {
-        this.localIndex = localIndex;
-    }
-    
-    @Parameters(name="localIndex = {0}")
-    public static Collection<Boolean[]> data() {
-        return Arrays.asList(new Boolean[][] {     
-                 { false }, { true }
-           });
-    }
-    
-    @Before
-    public void initTable() throws Exception {
-        final String url = getUrl();
-        final String index = localIndex ? "LOCAL INDEX" : "INDEX";
-        initATableValues(getOrganizationId(), null, url);
-        initSaltedTables(index);
-        initMultiTenantTables(index);
-        Connection connection = DriverManager.getConnection(url);
-        connection.createStatement().execute("CREATE " + index + " IF NOT EXISTS IDX1 ON aTable (a_string) INCLUDE (b_string, x_integer)");
-        connection.createStatement().execute("CREATE " + index + " IF NOT EXISTS IDX2 ON aTable (b_string) INCLUDE (a_string, y_integer)");
-        connection.createStatement().execute("CREATE " + index + " IF NOT EXISTS IDX_FULL ON aTable (b_string) INCLUDE (a_string, a_integer, a_date, a_time, a_timestamp, x_decimal, x_long, x_integer, y_integer, a_byte, a_short, a_float, a_double, a_unsigned_float, a_unsigned_double)");
-        connection.createStatement().execute("UPDATE STATISTICS ATABLE");
-        connection.createStatement().execute("UPDATE STATISTICS " + NOSALT_TABLE_NAME);
-        connection.createStatement().execute("UPDATE STATISTICS " + SALTED_TABLE_NAME);
-        connection.createStatement().execute("UPDATE STATISTICS " + MULTI_TENANT_TABLE);
-        connection.close();
-        
-        Properties props = new Properties();
-        props.setProperty("TenantId", "10");
-        connection = DriverManager.getConnection(url, props);
-        connection.createStatement().execute("UPDATE STATISTICS " + MULTI_TENANT_VIEW1);
-        connection.close();
-        
-        props.setProperty("TenantId", "20");
-        connection = DriverManager.getConnection(url, props);
-        connection.createStatement().execute("UPDATE STATISTICS " + MULTI_TENANT_VIEW2);
-        connection.close();
-    }
-    
-    @Test public void testIndex() throws Exception {
-        start(true, 1000f).sql("select * from aTable where b_string = 'b'")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(ORGANIZATION_ID=[$1], ENTITY_ID=[$2], A_STRING=[$3], B_STRING=[$0], A_INTEGER=[$4], A_DATE=[$5], A_TIME=[$6], A_TIMESTAMP=[$7], X_DECIMAL=[$8], X_LONG=[$9], X_INTEGER=[$10], Y_INTEGER=[$11], A_BYTE=[$12], A_SHORT=[$13], A_FLOAT=[$14], A_DOUBLE=[$15], A_UNSIGNED_FLOAT=[$16], A_UNSIGNED_DOUBLE=[$17])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
-            .close();
-        start(true, 1000f).sql("select x_integer from aTable")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(X_INTEGER=[$4])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX1]])\n")
-            .close();
-        start(true, 1000f).sql("select a_string from aTable order by a_string")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$0])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
-            .close();
-        start(true, 1000000f).sql("select a_string from aTable order by organization_id")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$2], ORGANIZATION_ID=[$0])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, ATABLE]], scanOrder=[FORWARD])\n")
-            .close();
-        start(true, 1000f).sql("select a_integer from aTable order by a_string")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerSort(sort0=[$1], dir0=[ASC])\n" +
-                       "    PhoenixServerProject(A_INTEGER=[$4], A_STRING=[$3])\n" +
-                       "      PhoenixTableScan(table=[[phoenix, IDX_FULL]])\n")
-            .close();
-        start(true, 1000f).sql("select a_string, b_string from aTable where a_string = 'a'")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$0], B_STRING=[$3])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX1]], filter=[=($0, 'a')])\n")
-            .close();
-        start(true, 1000f).sql("select a_string, b_string from aTable where b_string = 'b'")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$3], B_STRING=[$0])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX2]], filter=[=($0, 'b')])\n")
-            .close();
-        start(true, 1000f).sql("select a_string, b_string, x_integer, y_integer from aTable where b_string = 'b'")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$3], B_STRING=[$0], X_INTEGER=[$10], Y_INTEGER=[$11])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
-            .close();
-        start(true, 1000f).sql("select a_string, count(*) from aTable group by a_string")
-            .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerAggregate(group=[{0}], EXPR$1=[COUNT()], isOrdered=[true])\n" +
-                       "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
-            .close();
-    }
-    
-    @Test public void testSaltedIndex() throws Exception {
-        start(true, 1f).sql("select count(*) from " + NOSALT_TABLE_NAME + " where col0 > 3")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_NOSALT_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 3)])\n")
-                .resultIs(0, new Object[][]{{999L}})
-                .close();
-        start(true, 1f).sql("select mypk0, mypk1, col0 from " + NOSALT_TABLE_NAME + " where col0 <= 4")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[CAST($0):INTEGER])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_NOSALT_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 4)])\n")
-                .resultIs(0, new Object[][] {
-                        {2, 3, 4},
-                        {1, 2, 3}})
-                .close();
-        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " where mypk0 < 3")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[<($0, 3)])\n")
-                .resultIs(0, new Object[][] {
-                        {1, 2, 3, 4},
-                        {2, 3, 4, 5}})
-                .close();
-        start(true, 1f).sql("select count(*) from " + SALTED_TABLE_NAME + " where col0 > 3")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDX_SALTED_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 3)])\n")
-                .resultIs(0, new Object[][]{{999L}})
-                .close();
-        start(true, 1f).sql("select mypk0, mypk1, col0 from " + SALTED_TABLE_NAME + " where col0 <= 4")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[CAST($0):INTEGER])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDX_SALTED_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 4)])\n")
-                .resultIs(0, new Object[][] {
-                        {2, 3, 4},
-                        {1, 2, 3}})
-                .close();
-        start(true, 1f).sql("select count(*) from " + SALTED_TABLE_NAME + " where col1 > 4")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 4)])\n")
-                .resultIs(0, new Object[][]{{999L}})
-                .close();
-        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " where col1 <= 5 order by col1")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 5)], scanOrder=[FORWARD])\n")
-                .resultIs(new Object[][] {
-                        {1, 2, 3, 4},
-                        {2, 3, 4, 5}})
-                .close();
-        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk0 = s2.mypk0 and s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerJoin(condition=[AND(=($0, $4), =($1, $5))], joinType=[inner])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n" +
-                           "    PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n")
-                .resultIs(0, new Object[][] {
-                        {501, 502, 503, 504, 501, 502, 503, 504}})
-                .close();
-    }
-    
-    @Test public void testMultiTenant() throws Exception {
-        Properties props = getConnectionProps(true, 1f);
-        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where tenant_id = '10' and id <= '0004'")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[AND(=(CAST($0):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '10'), <=($1, '0004'))])\n")
-                .resultIs(0, new Object[][] {
-                        {"10", "0002", 3, 4, 5},
-                        {"10", "0003", 4, 5, 6},
-                        {"10", "0004", 5, 6, 7}})
-                .close();
-        
-        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where tenant_id = '20' and col1 < 8")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(TENANT_ID=[$0], ID=[$2], COL0=[$3], COL1=[CAST($1):INTEGER], COL2=[$4])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[AND(=(CAST($0):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '20'), <(CAST($1):INTEGER, 8))])\n")
-                .resultIs(0, new Object[][] {
-                        {"20", "0004", 5, 6, 7},
-                        {"20", "0005", 6, 7, 8}})
-                .close();
-        
-        try {
-            start(props).sql("select * from " + MULTI_TENANT_VIEW1)
-                .explainIs("")
-                .close();
-            fail("Should have got SQLException.");
-        } catch (SQLException e) {
-        }
-        
-        props.setProperty("TenantId", "15");
-        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where id = '0284'")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[=(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0284')])\n")
-                .resultIs(0, new Object[][] {
-                        {"0284", 285, 286, 287}})
-                .close();
-        
-        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where col1 > 1000")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(ID=[$1], COL0=[$2], COL1=[CAST($0):INTEGER], COL2=[$3])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 1000)])\n")
-                .resultIs(0, new Object[][] {
-                        {"0999", 1000, 1001, 1002},
-                        {"1000", 1001, 1002, 1003},
-                        {"1001", 1002, 1003, 1004},
-                        {"1002", 1003, 1004, 1005}})
-                .close();
-        
-        try {
-            start(props).sql("select * from " + MULTI_TENANT_VIEW1)
-                .explainIs("")
-                .close();
-            fail("Should have got SQLException.");
-        } catch (SQLException e) {
-        }
-
-        props.setProperty("TenantId", "10");
-        start(props).sql("select * from " + MULTI_TENANT_VIEW1 + " where id = '0512'")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[=(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0512')])\n")
-                .resultIs(0, new Object[][] {
-                        {"0512", 513, 514, 515}})
-                .close();
-        
-        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where col1 <= 6")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(ID=[$1], COL0=[$2], COL1=[CAST($0):INTEGER], COL2=[$3])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 6)])\n")
-                .resultIs(0, new Object[][] {
-                        {"0002", 3, 4, 5},
-                        {"0003", 4, 5, 6},
-                        {"0004", 5, 6, 7}})
-                .close();
-        
-        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW1 + " where col0 >= 1000")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, S1, IDX_MULTITENANT_TEST_VIEW1]], filter=[>=(CAST($0):INTEGER, 1000)])\n")
-                .resultIs(0, new Object[][] {
-                        {"0999", 1000},
-                        {"1000", 1001},
-                        {"1001", 1002}})
-                .close();
-
-        props.setProperty("TenantId", "20");
-        start(props).sql("select * from " + MULTI_TENANT_VIEW2 + " where id = '0765'")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[AND(>($3, 7), =(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0765'))])\n")
-                .resultIs(0, new Object[][] {
-                        {"0765", 766, 767, 768}})
-                .close();
-        
-        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW2 + " where col0 between 272 and 275")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, S2, IDX_MULTITENANT_TEST_VIEW2]], filter=[AND(>=(CAST($0):INTEGER, 272), <=(CAST($0):INTEGER, 275))])\n")
-                .resultIs(0, new Object[][] {
-                        {"0271", 272},
-                        {"0272", 273},
-                        {"0273", 274},
-                        {"0274", 275}})
-                .close();
-        
-        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW2 + " order by col0 limit 5")
-                .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixLimit(fetch=[5])\n" +
-                           "    PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, S2, IDX_MULTITENANT_TEST_VIEW2]], scanOrder=[FORWARD])\n")
-                .resultIs(new Object[][] {
-                        {"0005", 6},
-                        {"0006", 7},
-                        {"0007", 8},
-                        {"0008", 9},
-                        {"0009", 10}})
-                .close();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
new file mode 100644
index 0000000..30ec0bc
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.calcite;
+
+import static org.junit.Assert.fail;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.junit.Test;
+
+public class CalciteLocalIndexIT extends BaseCalciteIndexIT {
+    
+    public CalciteLocalIndexIT() {
+        super(true);
+    }
+    
+    @Test public void testIndex() throws Exception {
+        start(true, 1000f).sql("select * from aTable where b_string = 'b'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(ORGANIZATION_ID=[$1], ENTITY_ID=[$2], A_STRING=[$3], B_STRING=[$0], A_INTEGER=[$4], A_DATE=[$5], A_TIME=[$6], A_TIMESTAMP=[$7], X_DECIMAL=[$8], X_LONG=[$9], X_INTEGER=[$10], Y_INTEGER=[$11], A_BYTE=[$12], A_SHORT=[$13], A_FLOAT=[$14], A_DOUBLE=[$15], A_UNSIGNED_FLOAT=[$16], A_UNSIGNED_DOUBLE=[$17])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
+            .close();
+        start(true, 1000f).sql("select x_integer from aTable")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(X_INTEGER=[$10])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
+            /*.explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(X_INTEGER=[$4])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]])\n")*/
+            .close();
+        start(true, 1000f).sql("select a_string from aTable order by a_string")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$0])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
+            .close();
+        start(true, 1000000f).sql("select a_string from aTable order by organization_id")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$2], ORGANIZATION_ID=[$0])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, ATABLE]], scanOrder=[FORWARD])\n")
+            .close();
+        start(true, 1000f).sql("select a_integer from aTable order by a_string")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerSort(sort0=[$1], dir0=[ASC])\n" +
+                       "    PhoenixServerProject(A_INTEGER=[$4], A_STRING=[$2])\n" +
+                       "      PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
+            /*.explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerSort(sort0=[$1], dir0=[ASC])\n" +
+                       "    PhoenixServerProject(A_INTEGER=[$4], A_STRING=[$3])\n" +
+                       "      PhoenixTableScan(table=[[phoenix, IDX_FULL]])\n")*/
+            .close();
+        start(true, 1000f).sql("select a_string, b_string from aTable where a_string = 'a'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$0], B_STRING=[$3])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]], filter=[=($0, 'a')])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, b_string from aTable where b_string = 'b'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$3], B_STRING=[$0])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX2]], filter=[=($0, 'b')])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, b_string, x_integer, y_integer from aTable where b_string = 'b'")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerProject(A_STRING=[$3], B_STRING=[$0], X_INTEGER=[$10], Y_INTEGER=[$11])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX_FULL]], filter=[=($0, 'b')])\n")
+            .close();
+        start(true, 1000f).sql("select a_string, count(*) from aTable group by a_string")
+            .explainIs("PhoenixToEnumerableConverter\n" +
+                       "  PhoenixServerAggregate(group=[{0}], EXPR$1=[COUNT()], isOrdered=[true])\n" +
+                       "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
+            .close();
+    }
+    
+    @Test public void testSaltedIndex() throws Exception {
+        start(true, 1f).sql("select count(*) from " + NOSALT_TABLE_NAME + " where col0 > 3")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_NOSALT_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 3)])\n")
+                .resultIs(0, new Object[][]{{999L}})
+                .close();
+        start(true, 1f).sql("select mypk0, mypk1, col0 from " + NOSALT_TABLE_NAME + " where col0 <= 4")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_NOSALT_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 4)])\n")
+                .resultIs(0, new Object[][] {
+                        {2, 3, 4},
+                        {1, 2, 3}})
+                .close();
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " where mypk0 < 3")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[<($0, 3)])\n")
+                .resultIs(0, new Object[][] {
+                        {1, 2, 3, 4},
+                        {2, 3, 4, 5}})
+                .close();
+        start(true, 1f).sql("select count(*) from " + SALTED_TABLE_NAME + " where col0 > 3")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_SALTED_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 3)])\n")
+                .sameResultAsPhoenixStandalone(0)
+                /*.resultIs(0, new Object[][]{{999L}})*/
+                .close();
+        start(true, 1f).sql("select mypk0, mypk1, col0 from " + SALTED_TABLE_NAME + " where col0 <= 4")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_SALTED_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 4)])\n")
+                .resultIs(0, new Object[][] {
+                        {2, 3, 4},
+                        {1, 2, 3}})
+                .close();
+        start(true, 1f).sql("select count(*) from " + SALTED_TABLE_NAME + " where col1 > 4")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerAggregate(group=[{}], EXPR$0=[COUNT()])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 4)])\n")
+                .sameResultAsPhoenixStandalone(0)
+                /*.resultIs(0, new Object[][]{{999L}})*/
+                .close();
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " where col1 <= 5 order by col1")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 5)], scanOrder=[FORWARD])\n")
+                .resultIs(new Object[][] {
+                        {1, 2, 3, 4},
+                        {2, 3, 4, 5}})
+                .close();
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk0 = s2.mypk0 and s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixClientProject(MYPK0=[$4], MYPK1=[$5], COL0=[$6], COL1=[$7], MYPK00=[$0], MYPK10=[$1], COL00=[$2], COL10=[$3])\n" +
+                           "    PhoenixServerJoin(condition=[AND(=($4, $0), =($5, $1))], joinType=[inner])\n" +
+                           "      PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n")
+                /*.explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerJoin(condition=[AND(=($0, $4), =($1, $5))], joinType=[inner])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n" +
+                           "    PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n")*/
+                .resultIs(0, new Object[][] {
+                        {501, 502, 503, 504, 501, 502, 503, 504}})
+                .close();
+    }
+    
+    @Test public void testMultiTenant() throws Exception {
+        Properties props = getConnectionProps(true, 1f);
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where tenant_id = '10' and id <= '0004'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[AND(=(CAST($0):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '10'), <=($1, '0004'))])\n")
+                .resultIs(0, new Object[][] {
+                        {"10", "0002", 3, 4, 5},
+                        {"10", "0003", 4, 5, 6},
+                        {"10", "0004", 5, 6, 7}})
+                .close();
+        
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where tenant_id = '20' and col1 < 8")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(TENANT_ID=[$0], ID=[$2], COL0=[$3], COL1=[CAST($1):INTEGER], COL2=[$4])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[AND(=(CAST($0):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '20'), <(CAST($1):INTEGER, 8))])\n")
+//                .resultIs(0, new Object[][] {
+//                        {"20", "0004", 5, 6, 7},
+//                        {"20", "0005", 6, 7, 8}})
+                .close();
+        
+        try {
+            start(props).sql("select * from " + MULTI_TENANT_VIEW1)
+                .explainIs("")
+                .close();
+            fail("Should have got SQLException.");
+        } catch (SQLException e) {
+        }
+        
+        props.setProperty("TenantId", "15");
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where id = '0284'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[=(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0284')])\n")
+                .resultIs(0, new Object[][] {
+                        {"0284", 285, 286, 287}})
+                .close();
+        
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where col1 > 1000")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[$2], COL1=[CAST($0):INTEGER], COL2=[$3])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[>(CAST($0):INTEGER, 1000)])\n")
+//                .resultIs(0, new Object[][] {
+//                        {"0999", 1000, 1001, 1002},
+//                        {"1000", 1001, 1002, 1003},
+//                        {"1001", 1002, 1003, 1004},
+//                        {"1002", 1003, 1004, 1005}})
+                .close();
+        
+        try {
+            start(props).sql("select * from " + MULTI_TENANT_VIEW1)
+                .explainIs("")
+                .close();
+            fail("Should have got SQLException.");
+        } catch (SQLException e) {
+        }
+
+        props.setProperty("TenantId", "10");
+        start(props).sql("select * from " + MULTI_TENANT_VIEW1 + " where id = '0512'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[=(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0512')])\n")
+                .resultIs(0, new Object[][] {
+                        {"0512", 513, 514, 515}})
+                .close();
+        
+        start(props).sql("select * from " + MULTI_TENANT_TABLE + " where col1 <= 6")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[$2], COL1=[CAST($0):INTEGER], COL2=[$3])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, IDX_MULTITENANT_TEST_TABLE]], filter=[<=(CAST($0):INTEGER, 6)])\n")
+//                .resultIs(0, new Object[][] {
+//                        {"0002", 3, 4, 5},
+//                        {"0003", 4, 5, 6},
+//                        {"0004", 5, 6, 7}})
+                .close();
+        
+        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW1 + " where col0 >= 1000")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, S1, IDX_MULTITENANT_TEST_VIEW1]], filter=[>=(CAST($0):INTEGER, 1000)])\n")
+                .sameResultAsPhoenixStandalone(0)
+                /*.resultIs(0, new Object[][] {
+                        {"0999", 1000},
+                        {"1000", 1001},
+                        {"1001", 1002}})*/
+                .close();
+
+        props.setProperty("TenantId", "20");
+        start(props).sql("select * from " + MULTI_TENANT_VIEW2 + " where id = '0765'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixTableScan(table=[[phoenix, MULTITENANT_TEST_TABLE]], filter=[AND(>($3, 7), =(CAST($0):VARCHAR(4) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, '0765'))])\n")
+                .resultIs(0, new Object[][] {
+                        {"0765", 766, 767, 768}})
+                .close();
+        
+        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW2 + " where col0 between 272 and 275")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, S2, IDX_MULTITENANT_TEST_VIEW2]], filter=[AND(>=(CAST($0):INTEGER, 272), <=(CAST($0):INTEGER, 275))])\n")
+                .sameResultAsPhoenixStandalone(0)
+                /*.resultIs(0, new Object[][] {
+                        {"0271", 272},
+                        {"0272", 273},
+                        {"0273", 274},
+                        {"0274", 275}})*/
+                .close();
+        
+        start(props).sql("select id, col0 from " + MULTI_TENANT_VIEW2 + " order by col0 limit 5")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixLimit(fetch=[5])\n" +
+                           "    PhoenixServerProject(ID=[$1], COL0=[CAST($0):INTEGER])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, S2, IDX_MULTITENANT_TEST_VIEW2]], scanOrder=[FORWARD])\n")
+                .sameResultAsPhoenixStandalone()
+                /*.resultIs(new Object[][] {
+                        {"0005", 6},
+                        {"0006", 7},
+                        {"0007", 8},
+                        {"0008", 9},
+                        {"0009", 10}})*/
+                .close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/main/java/org/apache/calcite/jdbc/PhoenixCalciteFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/calcite/jdbc/PhoenixCalciteFactory.java b/phoenix-core/src/main/java/org/apache/calcite/jdbc/PhoenixCalciteFactory.java
index e769b08..5b9fd1d 100644
--- a/phoenix-core/src/main/java/org/apache/calcite/jdbc/PhoenixCalciteFactory.java
+++ b/phoenix-core/src/main/java/org/apache/calcite/jdbc/PhoenixCalciteFactory.java
@@ -25,6 +25,7 @@ import org.apache.calcite.avatica.UnregisteredDriver;
 import org.apache.calcite.jdbc.CalciteConnectionImpl;
 import org.apache.calcite.jdbc.CalciteFactory;
 import org.apache.calcite.jdbc.Driver;
+import org.apache.phoenix.calcite.PhoenixSchema;
 
 public class PhoenixCalciteFactory extends CalciteFactory {
     
@@ -95,6 +96,17 @@ public class PhoenixCalciteFactory extends CalciteFactory {
             super(driver, factory, url, info,
                     CalciteSchema.createRootSchema(true, false), typeFactory);
         }
+        
+        public void close() throws SQLException {
+            for (String subSchemaName : getRootSchema().getSubSchemaNames()) {               
+                try {
+                    PhoenixSchema phoenixSchema = getRootSchema()
+                            .getSubSchema(subSchemaName).unwrap(PhoenixSchema.class);
+                    phoenixSchema.pc.close();
+                } catch (ClassCastException e) {
+                }
+            }
+        }
     }
 
     private static class PhoenixCalciteStatement extends CalciteStatement {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/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
index 0b45322..1fcc9d3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
@@ -2,6 +2,7 @@ package org.apache.phoenix.calcite;
 
 import java.io.IOException;
 import java.sql.SQLException;
+import java.util.Iterator;
 import java.util.List;
 
 import org.apache.calcite.plan.RelOptTable;
@@ -40,6 +41,7 @@ import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.TableRef;
 import org.apache.phoenix.schema.stats.StatisticsUtil;
 import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.SchemaUtil;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -67,7 +69,12 @@ public class PhoenixTable extends AbstractTable implements TranslatableTable {
       
       List<PColumn> columns = Lists.newArrayList(pTable.getColumns());
       if (pTable.getViewIndexId() != null) {
-          columns.remove((pTable.getBucketNum() == null ? 0 : 1) + (pTable.isMultiTenant() ? 1 : 0));
+          for (Iterator<PColumn> iter = columns.iterator(); iter.hasNext();) {
+              if (iter.next().getName().getString().equals(MetaDataUtil.VIEW_INDEX_ID_COLUMN_NAME)) {
+                  iter.remove();
+                  break;
+              }
+          }
       }
       if (pTable.isMultiTenant()) {
           columns.remove(pTable.getBucketNum() == null ? 0 : 1);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixAbstractAggregate.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixAbstractAggregate.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixAbstractAggregate.java
index de9f0c2..16c97cc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixAbstractAggregate.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixAbstractAggregate.java
@@ -17,7 +17,6 @@ import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.ImmutableIntList;
-import org.apache.calcite.util.Util;
 import org.apache.phoenix.calcite.CalciteUtils;
 import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
 import org.apache.phoenix.compile.QueryPlan;
@@ -105,9 +104,10 @@ abstract public class PhoenixAbstractAggregate extends Aggregate implements Phoe
             return planner.getCostFactory().makeInfiniteCost();
         
         double rowCount = mq.getRowCount(this);
-        double bytesPerRow = mq.getAverageRowSize(this);
-        if (isOrderedGroupBy) {
-            rowCount = (rowCount * rowCount) / Util.nLogN(rowCount);
+        double spoolSize = 0;
+        if (!isOrderedGroupBy) {
+            double bytesPerRow = mq.getAverageRowSize(this);
+            spoolSize = rowCount * bytesPerRow * 6 /* map size */;
         }
         // Aggregates with more aggregate functions cost a bit more
         float multiplier = 1f + (float) aggCalls.size() * 0.125f;
@@ -118,7 +118,7 @@ abstract public class PhoenixAbstractAggregate extends Aggregate implements Phoe
             multiplier += 0.0125f;
           }
         }
-        return planner.getCostFactory().makeCost(rowCount * multiplier * bytesPerRow, 0, 0);
+        return planner.getCostFactory().makeCost(rowCount * multiplier + spoolSize, 0, 0);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
index 691b5ac..c510e3b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
@@ -206,7 +206,8 @@ public class PhoenixTableScan extends TableScan implements PhoenixRel {
             }
         }
         return planner.getCostFactory()
-                .makeCost(byteCount, byteCount + 1, 0)
+                .makeCost(byteCount + 1, byteCount + 1, 0)
+                .multiplyBy(0.5) /* data scan only */
                 .multiplyBy(SERVER_FACTOR)
                 .multiplyBy(PHOENIX_FACTOR);
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/caf1c6ed/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixToEnumerableConverter.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixToEnumerableConverter.java
index dd092a8..3d0aa22 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixToEnumerableConverter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixToEnumerableConverter.java
@@ -58,7 +58,10 @@ public class PhoenixToEnumerableConverter extends ConverterImpl implements Enume
     }
 
     @Override public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
-        return super.computeSelfCost(planner, mq).multiplyBy(.1);
+        return super.computeSelfCost(planner, mq)
+                .multiplyBy(.1)
+                .multiplyBy(PhoenixRel.PHOENIX_FACTOR)
+                .multiplyBy(PhoenixRel.SERVER_FACTOR);
     }
 
     @Override