phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ramkris...@apache.org
Subject [6/6] git commit: Phoenix-933 Local index support to Phoenix (Rajesh)
Date Wed, 16 Jul 2014 04:33:04 GMT
Phoenix-933 Local index support to Phoenix (Rajesh)


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

Branch: refs/heads/4.0
Commit: 9a5529c8a02f035fa81db3b7b2a4f0304bf59c1d
Parents: 5c03f65
Author: Ramkrishna <ramkrishna.s.vasudevan@intel.com>
Authored: Wed Jul 16 10:01:31 2014 +0530
Committer: Ramkrishna <ramkrishna.s.vasudevan@intel.com>
Committed: Wed Jul 16 10:01:31 2014 +0530

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    |  11 +-
 .../end2end/BaseTenantSpecificViewIndexIT.java  |  70 +-
 .../org/apache/phoenix/end2end/BaseViewIT.java  |  46 +-
 .../org/apache/phoenix/end2end/DeleteIT.java    |  78 ++-
 .../org/apache/phoenix/end2end/HashJoinIT.java  | 359 ++++++++++
 .../org/apache/phoenix/end2end/QueryIT.java     |  87 +--
 .../apache/phoenix/end2end/SaltedViewIT.java    |   7 +-
 .../end2end/TenantSpecificViewIndexIT.java      |  31 +-
 .../TenantSpecificViewIndexSaltedIT.java        |  10 +
 .../java/org/apache/phoenix/end2end/ViewIT.java |   2 +-
 .../phoenix/end2end/index/ImmutableIndexIT.java |  11 +-
 .../phoenix/end2end/index/LocalIndexIT.java     | 553 +++++++++++++++
 .../phoenix/end2end/index/MutableIndexIT.java   | 331 +++++++--
 .../phoenix/end2end/index/ViewIndexIT.java      |  85 +++
 .../index/balancer/TestIndexLoadBalancer.java   | 488 +++++++++++++
 phoenix-core/src/main/antlr3/PhoenixSQL.g       |   7 +-
 .../phoenix/compile/CreateIndexCompiler.java    |  19 +
 .../apache/phoenix/compile/DeleteCompiler.java  |   1 -
 .../phoenix/compile/ExpressionCompiler.java     |  21 +-
 .../apache/phoenix/compile/FromCompiler.java    |   2 +-
 .../phoenix/compile/IndexStatementRewriter.java |   1 -
 .../apache/phoenix/compile/JoinCompiler.java    |   9 +-
 .../phoenix/compile/StatementContext.java       |  38 +
 .../TrackOrderPreservingExpressionCompiler.java |   1 +
 .../apache/phoenix/compile/WhereCompiler.java   |  11 +
 .../coprocessor/BaseScannerRegionObserver.java  |   5 +
 .../GroupedAggregateRegionObserver.java         |  68 +-
 .../coprocessor/MetaDataEndpointImpl.java       |  11 +-
 .../phoenix/coprocessor/MetaDataProtocol.java   |   4 +-
 .../phoenix/coprocessor/ScanRegionObserver.java |  75 +-
 .../UngroupedAggregateRegionObserver.java       |  89 ++-
 .../coprocessor/generated/PTableProtos.java     | 107 ++-
 .../phoenix/exception/SQLExceptionCode.java     |   5 +-
 .../apache/phoenix/execute/BasicQueryPlan.java  | 154 +++++
 .../org/apache/phoenix/execute/ScanPlan.java    |   3 +-
 .../phoenix/expression/ExpressionType.java      |   3 +-
 .../expression/KeyValueColumnExpression.java    |   7 +
 .../expression/ProjectedColumnExpression.java   |  25 +-
 .../expression/RowKeyColumnExpression.java      |  12 +-
 .../function/SQLIndexTypeFunction.java          |  77 +++
 .../visitor/RowKeyExpressionVisitor.java        |  36 +
 .../apache/phoenix/filter/SkipScanFilter.java   |   7 +-
 .../hbase/index/IndexRegionSplitPolicy.java     |  32 +
 .../hbase/index/balancer/IndexLoadBalancer.java | 688 +++++++++++++++++++
 .../hbase/index/master/IndexMasterObserver.java |  69 ++
 .../hbase/index/util/IndexManagementUtil.java   |   4 +-
 .../apache/phoenix/index/IndexMaintainer.java   | 351 +++++++++-
 .../apache/phoenix/index/PhoenixIndexCodec.java |   4 +-
 .../DefaultParallelIteratorRegionSplitter.java  |  14 +-
 ...ocalIndexParallelIteratorRegionSplitter.java |  47 ++
 .../iterate/ParallelIteratorRegionSplitter.java |   3 +
 .../ParallelIteratorRegionSplitterFactory.java  |   4 +
 .../phoenix/iterate/ParallelIterators.java      |   9 +-
 .../phoenix/jdbc/PhoenixDatabaseMetaData.java   |   8 +-
 .../apache/phoenix/jdbc/PhoenixStatement.java   |  10 +-
 .../org/apache/phoenix/join/TupleProjector.java |  16 +-
 .../apache/phoenix/optimize/QueryOptimizer.java |  32 +-
 .../phoenix/parse/CreateIndexStatement.java     |  10 +-
 .../apache/phoenix/parse/ParseNodeFactory.java  |   5 +-
 .../apache/phoenix/parse/ParseNodeRewriter.java |   7 +-
 .../query/ConnectionQueryServicesImpl.java      |  88 ++-
 .../apache/phoenix/query/QueryConstants.java    |   2 +
 .../org/apache/phoenix/schema/ColumnRef.java    |  19 +-
 .../phoenix/schema/LocalIndexDataColumnRef.java |  55 ++
 .../apache/phoenix/schema/MetaDataClient.java   | 208 ++++--
 .../java/org/apache/phoenix/schema/PTable.java  |  37 +
 .../org/apache/phoenix/schema/PTableImpl.java   |  36 +-
 .../org/apache/phoenix/schema/SaltingUtil.java  |  16 +
 .../org/apache/phoenix/schema/ValueSchema.java  |  20 +-
 .../java/org/apache/phoenix/util/IndexUtil.java | 290 +++++++-
 .../org/apache/phoenix/util/MetaDataUtil.java   |  57 +-
 .../java/org/apache/phoenix/util/ScanUtil.java  |  53 +-
 .../phoenix/index/IndexMaintainerTest.java      |   8 +-
 .../java/org/apache/phoenix/query/BaseTest.java |  55 ++
 phoenix-protocol/src/main/PTable.proto          |   1 +
 75 files changed, 4848 insertions(+), 377 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index b822108..cdf405b 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -54,9 +54,10 @@ public class AlterTableIT extends BaseHBaseManagedTimeIT {
     public static final String SCHEMA_NAME = "";
     public static final String DATA_TABLE_NAME = "T";
     public static final String INDEX_TABLE_NAME = "I";
+    public static final String LOCAL_INDEX_TABLE_NAME = "LI";
     public static final String DATA_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "T");
     public static final String INDEX_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "I");
-
+    public static final String LOCAL_INDEX_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "LI");
 
     @Test
     public void testAlterTableWithVarBinaryKey() throws Exception {
@@ -208,6 +209,9 @@ public class AlterTableIT extends BaseHBaseManagedTimeIT {
     
         conn.createStatement().execute(
           "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+        conn.createStatement().execute(
+            "CREATE LOCAL INDEX " + LOCAL_INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+
         query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
@@ -266,9 +270,14 @@ public class AlterTableIT extends BaseHBaseManagedTimeIT {
     
         conn.createStatement().execute(
           "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) include (v2, v3)");
+        conn.createStatement().execute(
+            "CREATE LOCAL INDEX " + LOCAL_INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) include (v2, v3)");
         query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
+        query = "SELECT * FROM " + LOCAL_INDEX_TABLE_FULL_NAME;
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
     
         // load some data into the table
         stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?,?,?)");

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
index 5878806..b4ea8e5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
@@ -26,6 +26,8 @@ import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 
@@ -33,6 +35,8 @@ import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.QueryUtil;
 
+import com.google.common.collect.Lists;
+
 public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT {
     
     public static final String TENANT1_ID = "tenant1";
@@ -41,11 +45,15 @@ public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT {
     protected Set<Pair<String, String>> tenantViewsToDelete = newHashSet();
     
     protected void testUpdatableView(Integer saltBuckets) throws Exception {
+        testUpdatableView(saltBuckets, false);
+    }
+    
+    protected void testUpdatableView(Integer saltBuckets, boolean localIndex) throws Exception {
         createBaseTable("t", saltBuckets);
         Connection conn = createTenantConnection(TENANT1_ID);
         try {
             createAndPopulateTenantView(conn, TENANT1_ID, "t", "");
-            createAndVerifyIndex(conn, saltBuckets, TENANT1_ID, "");
+            createAndVerifyIndex(conn, saltBuckets, TENANT1_ID, "", localIndex);
             verifyViewData(conn, "");
         } finally {
             try { conn.close();} catch (Exception ignored) {}
@@ -53,6 +61,10 @@ public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT {
     }
     
     protected void testUpdatableViewsWithSameNameDifferentTenants(Integer saltBuckets) throws Exception {
+        testUpdatableViewsWithSameNameDifferentTenants(saltBuckets, false);
+    }
+
+    protected void testUpdatableViewsWithSameNameDifferentTenants(Integer saltBuckets, boolean localIndex) throws Exception {
         createBaseTable("t", saltBuckets);
         Connection conn1 = createTenantConnection(TENANT1_ID);
         Connection conn2 = createTenantConnection(TENANT2_ID);
@@ -64,8 +76,8 @@ public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT {
             createAndPopulateTenantView(conn1, TENANT1_ID, "t", prefixForTenant1Data);
             createAndPopulateTenantView(conn2, TENANT2_ID, "t", prefixForTenant2Data);
             
-            createAndVerifyIndex(conn1, saltBuckets, TENANT1_ID, prefixForTenant1Data);
-            createAndVerifyIndex(conn2, saltBuckets, TENANT2_ID, prefixForTenant2Data);
+            createAndVerifyIndex(conn1, saltBuckets, TENANT1_ID, prefixForTenant1Data, localIndex);
+            createAndVerifyIndex(conn2, saltBuckets, TENANT2_ID, prefixForTenant2Data, localIndex);
             
             verifyViewData(conn1, prefixForTenant1Data);
             verifyViewData(conn2, prefixForTenant2Data);
@@ -97,17 +109,26 @@ public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT {
         conn.commit();
     }
     
-    private void createAndVerifyIndex(Connection conn, Integer saltBuckets, String tenantId, String valuePrefix) throws SQLException {
-        conn.createStatement().execute("CREATE INDEX i ON v(v2)");
+    private void createAndVerifyIndex(Connection conn, Integer saltBuckets, String tenantId, String valuePrefix, boolean localIndex) throws SQLException {
+        if(localIndex){
+            conn.createStatement().execute("CREATE LOCAL INDEX i ON v(v2)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX i ON v(v2)");
+        }
         conn.createStatement().execute("UPSERT INTO v(k2,v1,v2) VALUES (-1, 'blah', 'superblah')"); // sanity check that we can upsert after index is there
         conn.commit();
         ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT k1, k2, v2 FROM v WHERE v2='" + valuePrefix + "v2-1'");
-        
-        String expected = saltBuckets == null ? 
-                "RANGE SCAN OVER _IDX_T ['" + tenantId + "',-32768,'" + valuePrefix + "v2-1']" :
-                "SKIP SCAN ON 3 KEYS OVER _IDX_T [0,'" + tenantId + "',-32768,'" + valuePrefix + "v2-1'] - [2,'" + tenantId + "',-32768,'" + valuePrefix + "v2-1']\n" + 
-                "CLIENT MERGE SORT";
-        assertTrue(QueryUtil.getExplainPlan(rs).contains(expected));
+        if(localIndex){
+            assertEquals(saltBuckets == null ? 
+                    "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_T ['" + tenantId + "',-32768,'" + valuePrefix + "v2-1']\nCLIENT MERGE SORT" :
+                        "CLIENT PARALLEL 3-WAY RANGE SCAN OVER _LOCAL_IDX_T ['" + tenantId + "',-32768,'" + valuePrefix + "v2-1']\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        } else {
+            String expected = saltBuckets == null ? 
+                    "RANGE SCAN OVER _IDX_T ['" + tenantId + "',-32768,'" + valuePrefix + "v2-1']" :
+                    "SKIP SCAN ON 3 KEYS OVER _IDX_T [0,'" + tenantId + "',-32768,'" + valuePrefix + "v2-1'] - [2,'" + tenantId + "',-32768,'" + valuePrefix + "v2-1']\n" + 
+                    "CLIENT MERGE SORT";
+            assertTrue(QueryUtil.getExplainPlan(rs).contains(expected));
+        }
     }
     
     private Connection createTenantConnection(String tenantId) throws SQLException {
@@ -119,26 +140,13 @@ public class BaseTenantSpecificViewIndexIT extends BaseHBaseManagedTimeIT {
     private void verifyViewData(Connection conn, String valuePrefix) throws SQLException {
         String query = "SELECT k1, k2, v2 FROM v WHERE v2='" + valuePrefix + "v2-1'";
         ResultSet rs = conn.createStatement().executeQuery(query);
-        assertTrue(rs.next());
-        assertEquals(1, rs.getInt(1));
-        assertEquals(1, rs.getInt(2));
-        assertEquals(valuePrefix + "v2-1", rs.getString(3));
-        assertTrue(rs.next());
-        assertEquals(1, rs.getInt(1));
-        assertEquals(3, rs.getInt(2));
-        assertEquals(valuePrefix + "v2-1", rs.getString(3));
-        assertTrue(rs.next());
-        assertEquals(1, rs.getInt(1));
-        assertEquals(5, rs.getInt(2));
-        assertEquals(valuePrefix + "v2-1", rs.getString(3));
-        assertTrue(rs.next());
-        assertEquals(1, rs.getInt(1));
-        assertEquals(7, rs.getInt(2));
-        assertEquals(valuePrefix + "v2-1", rs.getString(3));
-        assertTrue(rs.next());
-        assertEquals(1, rs.getInt(1));
-        assertEquals(9, rs.getInt(2));
-        assertEquals(valuePrefix + "v2-1", rs.getString(3));
+        List<List<Object>> expectedResultsA = Lists.newArrayList(
+            Arrays.<Object>asList(1,1, valuePrefix + "v2-1"),
+            Arrays.<Object>asList(1,3, valuePrefix + "v2-1"),
+            Arrays.<Object>asList(1,5, valuePrefix + "v2-1"),
+            Arrays.<Object>asList(1,7, valuePrefix + "v2-1"),
+            Arrays.<Object>asList(1,9, valuePrefix + "v2-1"));
+        assertValuesEqualsResultSet(rs,expectedResultsA);
         assertFalse(rs.next());
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java
index b125ab2..5d8df0f 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseViewIT.java
@@ -48,9 +48,9 @@ public class BaseViewIT extends BaseHBaseManagedTimeIT {
         setUpTestDriver(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
     }
 
-    protected void testUpdatableViewWithIndex(Integer saltBuckets) throws Exception {
+    protected void testUpdatableViewWithIndex(Integer saltBuckets, boolean localIndex) throws Exception {
         testUpdatableView(saltBuckets);
-        testUpdatableViewIndex(saltBuckets);
+        testUpdatableViewIndex(saltBuckets, localIndex);
     }
 
     protected void testUpdatableView(Integer saltBuckets) throws Exception {
@@ -100,9 +100,18 @@ public class BaseViewIT extends BaseHBaseManagedTimeIT {
     }
 
     protected void testUpdatableViewIndex(Integer saltBuckets) throws Exception {
+        testUpdatableViewIndex(saltBuckets, false);
+    }
+
+    protected void testUpdatableViewIndex(Integer saltBuckets, boolean localIndex) throws Exception {
         ResultSet rs;
         Connection conn = DriverManager.getConnection(getUrl());
-        conn.createStatement().execute("CREATE INDEX i1 on v(k3) include (s)");
+        if (localIndex) {
+            conn.createStatement().execute("CREATE LOCAL INDEX i1 on v(k3)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX i1 on v(k3) include (s)");
+        }
+        conn.createStatement().execute("UPSERT INTO v(k2,S,k3) VALUES(120,'foo',50.0)");
         String query = "SELECT k1, k2, k3, s FROM v WHERE k3 = 51.0";
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
@@ -112,12 +121,21 @@ public class BaseViewIT extends BaseHBaseManagedTimeIT {
         assertEquals("bar", rs.getString(4));
         assertFalse(rs.next());
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals(saltBuckets == null
-                ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_T [" + Short.MIN_VALUE + ",51]"
-                : "CLIENT PARALLEL " + saltBuckets + "-WAY SKIP SCAN ON 3 KEYS OVER _IDX_T [0," + Short.MIN_VALUE + ",51] - [2," + Short.MIN_VALUE + ",51]\nCLIENT MERGE SORT",
-            QueryUtil.getExplainPlan(rs));
+        if (localIndex) {
+            assertEquals("CLIENT PARALLEL 3-WAY RANGE SCAN OVER _LOCAL_IDX_T [-32768,51]\nCLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals(saltBuckets == null
+                    ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_T [" + Short.MIN_VALUE + ",51]"
+                            : "CLIENT PARALLEL " + saltBuckets + "-WAY SKIP SCAN ON 3 KEYS OVER _IDX_T [0," + Short.MIN_VALUE + ",51] - [2," + Short.MIN_VALUE + ",51]\nCLIENT MERGE SORT",
+                            QueryUtil.getExplainPlan(rs));
+        }
 
-        conn.createStatement().execute("CREATE INDEX i2 on v(s)");
+        if (localIndex) {
+            conn.createStatement().execute("CREATE LOCAL INDEX i2 on v(s)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX i2 on v(s)");
+        }
         query = "SELECT k1, k2, s FROM v WHERE s = 'foo'";
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
@@ -126,10 +144,14 @@ public class BaseViewIT extends BaseHBaseManagedTimeIT {
         assertEquals("foo", rs.getString(3));
         assertFalse(rs.next());
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals(saltBuckets == null
-                ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_T [" + (Short.MIN_VALUE+1) + ",'foo']"
-                : "CLIENT PARALLEL " + saltBuckets + "-WAY SKIP SCAN ON 3 KEYS OVER _IDX_T [0," + (Short.MIN_VALUE+1) + ",'foo'] - [2," + (Short.MIN_VALUE+1) + ",'foo']\nCLIENT MERGE SORT",
-            QueryUtil.getExplainPlan(rs));
+        if (localIndex) {
+            assertEquals("CLIENT PARALLEL 3-WAY RANGE SCAN OVER _LOCAL_IDX_T [" + (Short.MIN_VALUE+1) + ",'foo']\nCLIENT MERGE SORT",QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals(saltBuckets == null
+                    ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_T [" + (Short.MIN_VALUE+1) + ",'foo']"
+                            : "CLIENT PARALLEL " + saltBuckets + "-WAY SKIP SCAN ON 3 KEYS OVER _IDX_T [0," + (Short.MIN_VALUE+1) + ",'foo'] - [2," + (Short.MIN_VALUE+1) + ",'foo']\nCLIENT MERGE SORT",
+                            QueryUtil.getExplainPlan(rs));
+        }
     }
 
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/DeleteIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DeleteIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DeleteIT.java
index 4d41141..337e49b 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/DeleteIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/DeleteIT.java
@@ -32,6 +32,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.QueryUtil;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
@@ -147,14 +148,23 @@ public class DeleteIT extends BaseHBaseManagedTimeIT {
             String explainPlan = QueryUtil.getExplainPlan(rs);
             assertEquals(expectedToBeUsed, explainPlan.contains(" SCAN OVER " + indexName));
    }
-    
+
     private void testDeleteRange(boolean autoCommit, boolean createIndex) throws Exception {
+        testDeleteRange(autoCommit, createIndex, false);
+    }
+
+    private void testDeleteRange(boolean autoCommit, boolean createIndex, boolean local) throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
         initTableValues(conn);
         
         String indexName = "IDX";
         if (createIndex) {
-            conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx ON IntIntKeyTest(j)");
+            if (local) {
+                conn.createStatement().execute("CREATE LOCAL INDEX IF NOT EXISTS local_idx ON IntIntKeyTest(j)");
+                indexName = MetaDataUtil.getLocalIndexTableName("INTINTKEYTEST");
+            } else {
+                conn.createStatement().execute("CREATE INDEX IF NOT EXISTS idx ON IntIntKeyTest(j)");
+            }
         }
         
         ResultSet rs;
@@ -217,17 +227,32 @@ public class DeleteIT extends BaseHBaseManagedTimeIT {
     
     @Test
     public void testDeleteRangeNoAutoCommitWithIndex() throws Exception {
-        testDeleteRange(false, true);
+        testDeleteRange(false, true, false);
+    }
+
+    @Test
+    public void testDeleteRangeNoAutoCommitWithLocalIndexIndex() throws Exception {
+        testDeleteRange(false, true, true);
     }
     
     @Test
     public void testDeleteRangeAutoCommitWithIndex() throws Exception {
-        testDeleteRange(true, true);
+        testDeleteRange(true, true, false);
     }
     
     @Test
+    public void testDeleteRangeAutoCommitWithLocalIndex() throws Exception {
+        testDeleteRange(true, true, true);
+    }
+
+    @Test
     public void testDeleteAllFromTableWithIndexAutoCommitSalting() throws SQLException {
-        testDeleteAllFromTableWithIndex(true, true);
+        testDeleteAllFromTableWithIndex(true, true, false);
+    }
+
+    @Test
+    public void testDeleteAllFromTableWithLocalIndexAutoCommitSalting() throws SQLException {
+        testDeleteAllFromTableWithIndex(true, true, true);
     }
     
     @Test
@@ -242,26 +267,40 @@ public class DeleteIT extends BaseHBaseManagedTimeIT {
     
     @Test
     public void testDeleteAllFromTableWithIndexNoAutoCommitSalted() throws SQLException {
-        testDeleteAllFromTableWithIndex(false, true);
+        testDeleteAllFromTableWithIndex(false, true, false);
     }
     
+    @Test
+    public void testDeleteAllFromTableWithLocalIndexNoAutoCommitSalted() throws SQLException {
+        testDeleteAllFromTableWithIndex(false, true, true);
+    }
+
     private void testDeleteAllFromTableWithIndex(boolean autoCommit, boolean isSalted) throws SQLException {
+        testDeleteAllFromTableWithIndex(autoCommit, isSalted, false);
+    }
+
+    private void testDeleteAllFromTableWithIndex(boolean autoCommit, boolean isSalted, boolean localIndex) throws SQLException {
         Connection con = null;
         try {
             con = DriverManager.getConnection(getUrl());
             con.setAutoCommit(autoCommit);
 
             Statement stm = con.createStatement();
-            stm.execute("CREATE TABLE IF NOT EXISTS web_stats (" +
-            		"HOST CHAR(2) NOT NULL," +
-            		"DOMAIN VARCHAR NOT NULL, " +
-            		"FEATURE VARCHAR NOT NULL, " +
-            		"DATE DATE NOT NULL, \n" + 
-            		"USAGE.CORE BIGINT," +
-            		"USAGE.DB BIGINT," +
-            		"STATS.ACTIVE_VISITOR INTEGER " +
-            		"CONSTRAINT PK PRIMARY KEY (HOST, DOMAIN, FEATURE, DATE))" + (isSalted ? " SALT_BUCKETS=3" : ""));
-            stm.execute("CREATE INDEX web_stats_idx ON web_stats (CORE,DB,ACTIVE_VISITOR)");
+            String s = "CREATE TABLE IF NOT EXISTS web_stats (" +
+                    "HOST CHAR(2) NOT NULL," +
+                    "DOMAIN VARCHAR NOT NULL, " +
+                    "FEATURE VARCHAR NOT NULL, " +
+                    "DATE DATE NOT NULL, \n" + 
+                    "USAGE.CORE BIGINT," +
+                    "USAGE.DB BIGINT," +
+                    "STATS.ACTIVE_VISITOR INTEGER " +
+                    "CONSTRAINT PK PRIMARY KEY (HOST, DOMAIN, FEATURE, DATE))" + (isSalted ? " SALT_BUCKETS=3" : "");
+            stm.execute(s);
+            if (localIndex) {
+                stm.execute("CREATE LOCAL INDEX local_web_stats_idx ON web_stats (CORE,DB,ACTIVE_VISITOR)");
+            } else {
+                stm.execute("CREATE INDEX web_stats_idx ON web_stats (CORE,DB,ACTIVE_VISITOR)");
+            }
             stm.close();
 
             PreparedStatement psInsert = con
@@ -287,8 +326,11 @@ public class DeleteIT extends BaseHBaseManagedTimeIT {
             ResultSet rs = con.createStatement().executeQuery("SELECT /*+ NO_INDEX */ count(*) FROM web_stats");
             assertTrue(rs.next());
             assertEquals(0, rs.getLong(1));
-
-            rs = con.createStatement().executeQuery("SELECT count(*) FROM web_stats_idx");
+            if(localIndex){
+                rs = con.createStatement().executeQuery("SELECT count(*) FROM local_web_stats_idx");
+            } else {
+                rs = con.createStatement().executeQuery("SELECT count(*) FROM web_stats_idx");
+            }
             assertTrue(rs.next());
             assertEquals(0, rs.getLong(1));
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
index 26c9455..99a3d9d 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
@@ -51,6 +51,7 @@ import java.util.Properties;
 import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.schema.TableAlreadyExistsException;
+import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.junit.Before;
@@ -777,6 +778,364 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
                 "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
                 "    JOIN-SCANNER 4 ROW LIMIT",
                 }});
+        testCases.add(new String[][] {
+                {
+                "CREATE LOCAL INDEX \"idx_customer\" ON " + JOIN_CUSTOMER_TABLE_FULL_NAME + " (name)",
+                "CREATE LOCAL INDEX \"idx_item\" ON " + JOIN_ITEM_TABLE_FULL_NAME + " (name) INCLUDE (price, discount1, discount2, \"supplier_id\", description)",
+                "CREATE LOCAL INDEX \"idx_supplier\" ON " + JOIN_SUPPLIER_TABLE_FULL_NAME + " (name)"
+                }, {
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.0:NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [I.0:NAME]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME +"\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     LEFT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC"
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.:item_id]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME +"\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",          
+                /* 
+                 * testLeftJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinItemTable i 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
+                /* 
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME+"\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.0:NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [I.0:NAME]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
+                /*
+                 * testRightJoinWithAggregation()
+                 *     SELECT i.item_id iid, sum(quantity) q FROM joinOrderTable o 
+                 *     RIGHT JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     GROUP BY i.item_id ORDER BY q DESC NULLS LAST, iid
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [I.item_id]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC NULLS LAST, I.item_id]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME,
+                /*
+                 * testJoinWithWildcard()
+                 *     SELECT * FROM joinItemTable LEFT JOIN joinSupplierTable supp 
+                 *     ON joinItemTable.supplier_id = supp.supplier_id 
+                 *     ORDER BY item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item LEFT JOIN joinSupplierTable supp 
+                 *     ON substr(item.name, 2, 1) = substr(supp.name, 2, 1) 
+                 *         AND (supp.name BETWEEN 'S1' AND 'S5') 
+                 *     WHERE item.name BETWEEN 'T1' AND 'T5'
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768,'T1'] - [-32768,'T5']\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_SUPPLIER_TABLE_DISPLAY_NAME +" [-32768,'S1'] - [-32768,'S5']\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testJoinPlanWithIndex()
+                 *     SELECT item.item_id, item.name, supp.supplier_id, supp.name 
+                 *     FROM joinItemTable item INNER JOIN joinSupplierTable supp 
+                 *     ON item.supplier_id = supp.supplier_id 
+                 *     WHERE (item.name = 'T1' OR item.name = 'T5') 
+                 *         AND (supp.name = 'S1' OR supp.name = 'S5')
+                 */
+                "CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768,'T1'] - [-32768,'T5']\n" +
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY SKIP SCAN ON 2 KEYS OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_SUPPLIER_TABLE_DISPLAY_NAME +" [-32768,'S1'] - [-32768,'S5']\n" + 
+                "        CLIENT MERGE SORT",
+                /*
+                 * testJoinWithSkipMergeOptimization()
+                 *     SELECT s.name FROM joinItemTable i 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id AND quantity < 5000 
+                 *     JOIN joinSupplierTable s ON i.supplier_id = s.supplier_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "CLIENT MERGE SORT\n" + 
+                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER FILTER BY QUANTITY < 5000\n" +
+                "    BUILD HASH TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i2.item_id, i1.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.item_id 
+                 *     ORDER BY i1.item_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +"\n"  +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testSelfJoin()
+                 *     SELECT i1.name, i2.name FROM joinItemTable i1 
+                 *     JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id 
+                 *     ORDER BY i1.name, i2.name
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +"\n"  +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [I1.0:NAME, I2.0:NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +"\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testStarJoin()
+                 *     SELECT order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + "\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    BUILD HASH TABLE 1\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testStarJoin()
+                 *     SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date 
+                 *     FROM joinOrderTable o 
+                 *     JOIN joinCustomerTable c ON o.customer_id = c.customer_id 
+                 *     JOIN joinItemTable i ON o.item_id = i.item_id 
+                 *     ORDER BY order_id
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.order_id]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            BUILD HASH TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX + "" + JOIN_CUSTOMER_TABLE_DISPLAY_NAME+"\n"+
+                "                CLIENT MERGE SORT",
+                /*
+                 * testSubJoin()
+                 *     SELECT * FROM joinCustomerTable c 
+                 *     INNER JOIN (joinOrderTable o 
+                 *         INNER JOIN (joinSupplierTable s 
+                 *             RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
+                 *         ON o.item_id = i.item_id)
+                 *     ON c.customer_id = o.customer_id
+                 *     WHERE c.customer_id <= '0000000005' 
+                 *         AND order_id != '000000000000003' 
+                 *         AND i.name != 'T3' 
+                 *     ORDER BY c.customer_id, i.name
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [C.customer_id, I.0:NAME]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER FILTER BY order_id != '000000000000003'\n" +
+                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            BUILD HASH TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +"\n" +
+                "                    SERVER FILTER BY NAME != 'T3'\n" +
+                "                CLIENT MERGE SORT\n" +
+                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    BUILD HASH TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.name, sum(quantity) FROM joinOrderTable o 
+                 *     LEFT JOIN (SELECT name, item_id iid FROM joinItemTable) AS i 
+                 *     ON o.item_id = i.iid 
+                 *     GROUP BY i.name ORDER BY i.name
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [I.NAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [I.NAME]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME+"\n"+
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT o.iid, sum(o.quantity) q 
+                 *     FROM (SELECT item_id iid, quantity FROM joinOrderTable) AS o 
+                 *     LEFT JOIN (SELECT item_id FROM joinItemTable) AS i 
+                 *     ON o.iid = i.item_id 
+                 *     GROUP BY o.iid ORDER BY q DESC                 
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER AGGREGATE INTO DISTINCT ROWS BY [O.IID]\n" +
+                "CLIENT MERGE SORT\n" +
+                "CLIENT SORTED BY [SUM(O.QUANTITY) DESC]\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0 (SKIP MERGE)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER FILTER BY FIRST KEY ONLY\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     LEFT JOIN (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC NULLS LAST, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC NULLS LAST, I.IID]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
+                "        CLIENT MERGE SORT",
+                /* 
+                 * testJoinWithSubqueryAndAggregation()
+                 *     SELECT i.iid, o.q 
+                 *     FROM (SELECT item_id iid, sum(quantity) q FROM joinOrderTable GROUP BY item_id) AS o 
+                 *     JOIN (SELECT item_id iid FROM joinItemTable) AS i 
+                 *     ON o.iid = i.iid 
+                 *     ORDER BY o.q DESC, i.iid
+                 */     
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER FILTER BY FIRST KEY ONLY\n" +
+                "    SERVER SORTED BY [O.Q DESC, I.IID]\n"+
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER AGGREGATE INTO DISTINCT ROWS BY [item_id]\n" +
+                "        CLIENT MERGE SORT",
+                /*
+                 * testNestedSubqueries()
+                 *     SELECT * FROM (SELECT customer_id cid, name, phone, address, loc_id, date FROM joinCustomerTable) AS c 
+                 *     INNER JOIN (SELECT o.oid ooid, o.cid ocid, o.iid oiid, o.price * o.quantity, o.date odate, 
+                 *     qi.iiid iiid, qi.iname iname, qi.iprice iprice, qi.idiscount1 idiscount1, qi.idiscount2 idiscount2, qi.isid isid, qi.idescription idescription, 
+                 *     qi.ssid ssid, qi.sname sname, qi.sphone sphone, qi.saddress saddress, qi.sloc_id sloc_id 
+                 *         FROM (SELECT item_id iid, customer_id cid, order_id oid, price, quantity, date FROM joinOrderTable) AS o 
+                 *         INNER JOIN (SELECT i.iid iiid, i.name iname, i.price iprice, i.discount1 idiscount1, i.discount2 idiscount2, i.sid isid, i.description idescription, 
+                 *         s.sid ssid, s.name sname, s.phone sphone, s.address saddress, s.loc_id sloc_id 
+                 *             FROM (SELECT supplier_id sid, name, phone, address, loc_id FROM joinSupplierTable) AS s 
+                 *             RIGHT JOIN (SELECT item_id iid, name, price, discount1, discount2, supplier_id sid, description FROM joinItemTable) AS i 
+                 *             ON i.sid = s.sid) as qi 
+                 *         ON o.iid = qi.iiid) as qo 
+                 *     ON c.cid = qo.ocid 
+                 *     WHERE c.cid <= '0000000005' 
+                 *         AND qo.ooid != '000000000000003' 
+                 *         AND qo.iname != 'T3' 
+                 *     ORDER BY c.cid, qo.iname
+                 */
+                "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
+                "    SERVER SORTED BY [C.CID, QO.INAME]\n" +
+                "CLIENT MERGE SORT\n" +
+                "    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "            SERVER FILTER BY order_id != '000000000000003'\n" +
+                "            PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "            BUILD HASH TABLE 0\n" +
+                "                CLIENT PARALLEL 1-WAY FULL SCAN OVER " +  MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "                    SERVER FILTER BY NAME != 'T3'\n" +
+                "                CLIENT MERGE SORT\n" +      
+                "                    PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+                "                    BUILD HASH TABLE 0\n" +
+                "                        CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     LEFT JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     LEFT JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
+                "    SERVER FILTER BY PageFilter 4\n" +
+                "    SERVER 4 ROW LIMIT\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "        CLIENT MERGE SORT\n" +      
+                "    BUILD HASH TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                /*
+                 * testJoinWithLimit()
+                 *     SELECT order_id, i.name, s.name, s.address, quantity 
+                 *     FROM joinSupplierTable s 
+                 *     JOIN joinItemTable i ON i.supplier_id = s.supplier_id 
+                 *     JOIN joinOrderTable o ON o.item_id = i.item_id LIMIT 4
+                 */
+                "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME + "\n" +
+                "CLIENT 4 ROW LIMIT\n" +
+                "    PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
+                "    BUILD HASH TABLE 0\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+                "        CLIENT MERGE SORT\n" +
+                "    BUILD HASH TABLE 1(DELAYED EVALUATION)\n" +
+                "        CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+                "    JOIN-SCANNER 4 ROW LIMIT",
+                }});
         return testCases;
     }
     

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java
index f453853..a1a9ba7 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryIT.java
@@ -51,9 +51,9 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
 import org.apache.hadoop.hbase.client.HTable;
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
@@ -66,6 +66,7 @@ import org.apache.phoenix.schema.ConstraintViolationException;
 import org.apache.phoenix.schema.PDataType;
 import org.apache.phoenix.schema.SequenceNotFoundException;
 import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.junit.Before;
@@ -78,7 +79,6 @@ import org.junit.runners.Parameterized.Parameters;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 
 
 
@@ -143,6 +143,8 @@ public class QueryIT extends BaseClientManagedTimeIT {
                 + "    B_STRING, " + "    A_DATE)" });
         testCases.add(new String[] { "CREATE INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer) INCLUDE ("
                 + "    A_STRING, " + "    B_STRING, " + "    A_DATE)" });
+        testCases.add(new String[] { "CREATE LOCAL INDEX " + ATABLE_INDEX_NAME + " ON aTable (a_integer, a_string) INCLUDE ("
+                + "    B_STRING, " + "    A_DATE)" });
         testCases.add(new String[] { "" });
         return testCases;
     }
@@ -154,59 +156,6 @@ public class QueryIT extends BaseClientManagedTimeIT {
         }
         assertValuesEqualsResultSet(rs, nestedExpectedResults); 
     }
-
-    /**
-     * Asserts that we find the expected values in the result set. We don't know the order, since we don't always
-     * have an order by and we're going through indexes, but we assert that each expected result occurs once as
-     * expected (in any order).
-     */
-    protected void assertValuesEqualsResultSet(ResultSet rs, List<List<Object>> expectedResults) throws SQLException {
-        int expectedCount = expectedResults.size();
-        int count = 0;
-        List<List<Object>> actualResults = Lists.newArrayList();
-        List<Object> errorResult = null;
-        while (rs.next() && errorResult == null) {
-            List<Object> result = Lists.newArrayList();
-            for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
-                result.add(rs.getObject(i+1));
-            }
-            if (!expectedResults.contains(result)) {
-                errorResult = result;
-            }
-            actualResults.add(result);
-            count++;
-        }
-        assertTrue("Could not find " + errorResult + " in expected results: " + expectedResults + " with actual results: " + actualResults, errorResult == null);
-        assertEquals(count, expectedCount);
-    }
-    
-    protected void assertOneOfValuesEqualsResultSet(ResultSet rs, List<List<Object>>... expectedResultsArray) throws SQLException {
-        List<List<Object>> results = Lists.newArrayList();
-        while (rs.next()) {
-            List<Object> result = Lists.newArrayList();
-            for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
-                result.add(rs.getObject(i+1));
-            }
-            results.add(result);
-        }
-        for (int j = 0; j < expectedResultsArray.length; j++) {
-                List<List<Object>> expectedResults = expectedResultsArray[j];
-                Set<List<Object>> expectedResultsSet = Sets.newHashSet(expectedResults);
-                int count = 0;
-                boolean brokeEarly = false;
-                for (List<Object> result : results) {
-                    if (!expectedResultsSet.contains(result)) {
-                        brokeEarly = true;
-                        break;
-                    }
-                    count++;
-                }
-                if (!brokeEarly && count == expectedResults.size()) {
-                    return;
-                }
-        }
-        fail("Unable to find " + results + " in " + Arrays.asList(expectedResultsArray));
-    }
     
     @Test
     public void testIntFilter() throws Exception {
@@ -771,8 +720,8 @@ public class QueryIT extends BaseClientManagedTimeIT {
         String[] answers = new String[]{"00D300000000XHP5bar","a5bar","15bar","5bar","5bar"};
         String[] queries = new String[] { 
         		"SELECT  organization_id || 5 || 'bar' FROM atable limit 1",
-        		"SELECT a_string || 5 || 'bar' FROM atable limit 1",
-        		"SELECT a_integer||5||'bar' FROM atable limit 1",
+        		"SELECT a_string || 5 || 'bar' FROM atable  order by a_string  limit 1",
+        		"SELECT a_integer||5||'bar' FROM atable order by a_integer  limit 1",
         		"SELECT x_decimal||5||'bar' FROM atable limit 1",
         		"SELECT x_long||5||'bar' FROM atable limit 1"
         };
@@ -879,17 +828,19 @@ public class QueryIT extends BaseClientManagedTimeIT {
             
             byte[] tableName = Bytes.toBytes(ATABLE_NAME);
             admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin();
-            HTable htable = (HTable) conn.unwrap(PhoenixConnection.class).getQueryServices().getTable(tableName);
-            htable.clearRegionCache();
-            int nRegions = htable.getRegionLocations().size();
-            admin.split(tableName, ByteUtil.concat(Bytes.toBytes(tenantId), Bytes.toBytes("00A" + Character.valueOf((char) ('3' + nextRunCount())) + ts))); // vary split point with test run
-            int retryCount = 0;
-            do {
-                Thread.sleep(2000);
-                retryCount++;
-                //htable.clearRegionCache();
-            } while (retryCount < 10 && htable.getRegionLocations().size() == nRegions);
-            assertNotEquals(nRegions, htable.getRegionLocations().size());
+            if (admin.tableExists(TableName.valueOf(MetaDataUtil.getLocalIndexTableName("atable")))) {
+                HTable htable = (HTable) conn.unwrap(PhoenixConnection.class).getQueryServices().getTable(tableName);
+                htable.clearRegionCache();
+                int nRegions = htable.getRegionLocations().size();
+                admin.split(tableName, ByteUtil.concat(Bytes.toBytes(tenantId), Bytes.toBytes("00A" + Character.valueOf((char) ('3' + nextRunCount())) + ts))); // vary split point with test run
+                int retryCount = 0;
+                do {
+                    Thread.sleep(2000);
+                    retryCount++;
+                    //htable.clearRegionCache();
+                } while (retryCount < 10 && htable.getRegionLocations().size() == nRegions);
+                assertNotEquals(nRegions, htable.getRegionLocations().size());
+            }
             
             statement.setString(1, tenantId);
             rs = statement.executeQuery();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/SaltedViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SaltedViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SaltedViewIT.java
index ea59b85..0db0408 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SaltedViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SaltedViewIT.java
@@ -32,6 +32,11 @@ public class SaltedViewIT extends BaseViewIT {
      */
     @Test
     public void testSaltedUpdatableViewWithIndex() throws Exception {
-        testUpdatableViewWithIndex(3);
+        testUpdatableViewWithIndex(3, false);
+    }
+
+    @Test
+    public void testSaltedUpdatableViewWithLocalIndex() throws Exception {
+        testUpdatableViewWithIndex(3, true);
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
index cec7fed..5452d38 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java
@@ -42,12 +42,31 @@ public class TenantSpecificViewIndexIT extends BaseTenantSpecificViewIndexIT {
     }
 
     @Test
+    public void testUpdatableViewLocalIndex() throws Exception {
+        testUpdatableView(null, true);
+    }
+
+    @Test
     public void testUpdatableViewsWithSameNameDifferentTenants() throws Exception {
         testUpdatableViewsWithSameNameDifferentTenants(null);
     }
 
     @Test
+    public void testUpdatableViewsWithSameNameDifferentTenantsWithLocalIndex() throws Exception {
+        testUpdatableViewsWithSameNameDifferentTenants(null, true);
+    }
+
+    @Test
     public void testMultiCFViewIndex() throws Exception {
+        testMultiCFViewIndex(false);
+    }
+
+    @Test
+    public void testMultiCFViewLocalIndex() throws Exception {
+        testMultiCFViewIndex(true);
+    }
+    
+    private void testMultiCFViewIndex(boolean localIndex) throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
         String ddl = "CREATE TABLE MT_BASE (PK1 VARCHAR not null, PK2 VARCHAR not null, "
                 + "MYCF1.COL1 varchar,MYCF2.COL2 varchar "
@@ -76,7 +95,11 @@ public class TenantSpecificViewIndexIT extends BaseTenantSpecificViewIndexIT {
         assertFalse(rs.next());
         conn.createStatement().execute("UPSERT INTO acme VALUES ('e','f','g')");
         conn.commit();
-        conn.createStatement().execute("create index idx_acme on acme (COL1)");
+        if(localIndex){
+            conn.createStatement().execute("create local index idx_acme on acme (COL1)");
+        } else {
+            conn.createStatement().execute("create index idx_acme on acme (COL1)");
+        }
         rs = conn.createStatement().executeQuery("select * from acme");
         assertTrue(rs.next());
         assertEquals("b",rs.getString(1));
@@ -96,7 +119,11 @@ public class TenantSpecificViewIndexIT extends BaseTenantSpecificViewIndexIT {
         assertEquals("f",rs.getString(2));
         assertFalse(rs.next());
         rs = conn.createStatement().executeQuery("explain select pk2,col1 from acme where col1='f'");
-        assertEquals("CLIENT PARALLEL 4-WAY RANGE SCAN OVER _IDX_MT_BASE ['a',-32768,'f']",QueryUtil.getExplainPlan(rs));
+        if(localIndex){
+            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_MT_BASE ['a',-32768,'f']\nCLIENT MERGE SORT",QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 4-WAY RANGE SCAN OVER _IDX_MT_BASE ['a',-32768,'f']",QueryUtil.getExplainPlan(rs));
+        }
         
         try {
             // Cannot reference tenant_id column in tenant specific connection

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
index 388aaef..da500fe 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexSaltedIT.java
@@ -37,4 +37,14 @@ public class TenantSpecificViewIndexSaltedIT extends BaseTenantSpecificViewIndex
     public void testUpdatableViewsWithSameNameDifferentTenants() throws Exception {
         testUpdatableViewsWithSameNameDifferentTenants(SALT_BUCKETS);
     }
+    
+    @Test
+    public void testUpdatableSaltedViewWithLocalIndex() throws Exception {
+        testUpdatableView(SALT_BUCKETS, true);
+    }
+
+    @Test
+    public void testUpdatableViewsWithSameNameDifferentTenantsWithLocalIndex() throws Exception {
+        testUpdatableViewsWithSameNameDifferentTenants(SALT_BUCKETS, true);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
index b75bb30..1d022e5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
@@ -95,7 +95,7 @@ public class ViewIT extends BaseViewIT {
 
     @Test
     public void testNonSaltedUpdatableViewWithIndex() throws Exception {
-        testUpdatableViewWithIndex(null);
+        testUpdatableViewWithIndex(null, false);
     }
     
     @Test

http://git-wip-us.apache.org/repos/asf/phoenix/blob/9a5529c8/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java
index 9128595..698ade0 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java
@@ -118,8 +118,8 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT {
         ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
         populateTestTable();
         String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
-                + " (char_col1 ASC, int_col1 ASC)"
-                + " INCLUDE (long_col1, long_col2)";
+                    + " (char_col1 ASC, int_col1 ASC)"
+                    + " INCLUDE (long_col1, long_col2)";
         PreparedStatement stmt = conn.prepareStatement(ddl);
         stmt.execute();
         
@@ -196,8 +196,8 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT {
         ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
         populateTestTable();
         String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
-                + " (long_pk, varchar_pk)"
-                + " INCLUDE (long_col1, long_col2)";
+                    + " (long_pk, varchar_pk)"
+                    + " INCLUDE (long_col1, long_col2)";
         PreparedStatement stmt = conn.prepareStatement(ddl);
         stmt.execute();
         
@@ -242,7 +242,6 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT {
         conn.createStatement().execute("DROP INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE);
     }
     
-    
     @Test
     public void testDropIfImmutableKeyValueColumn() throws Exception {
         Properties props = new Properties(TEST_PROPERTIES);
@@ -251,7 +250,7 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT {
         ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
         populateTestTable();
         String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
-                + " (long_col1)";
+                    + " (long_col1)";
         PreparedStatement stmt = conn.prepareStatement(ddl);
         stmt.execute();
         


Mime
View raw message