You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ra...@apache.org on 2014/07/15 06:35:46 UTC

[5/6] PHOENIX-933 Local index support to Phoenix (Ram)

http://git-wip-us.apache.org/repos/asf/phoenix/blob/247ac8ee/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalIndexIT.java
new file mode 100644
index 0000000..5f7691b
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/LocalIndexIT.java
@@ -0,0 +1,553 @@
+/*
+ * 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.end2end.index;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.HTableDescriptor;
+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.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.coprocessor.BaseScannerRegionObserver;
+import org.apache.phoenix.hbase.index.IndexRegionSplitPolicy;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTable.IndexType;
+import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.util.MetaDataUtil;
+import org.apache.phoenix.util.QueryUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+
+public class LocalIndexIT extends BaseIndexIT {
+
+    @BeforeClass 
+    public static void doSetup() throws Exception {
+        Map<String,String> props = Maps.newHashMapWithExpectedSize(3);
+        // Drop the HBase table metadata for this test
+        props.put(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
+        // Must update config before starting server
+        setUpTestDriver(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+    private void createBaseTable(String tableName, Integer saltBuckets, String splits) throws SQLException {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE " + tableName + " (t_id VARCHAR NOT NULL,\n" +
+                "k1 INTEGER NOT NULL,\n" +
+                "k2 INTEGER NOT NULL,\n" +
+                "k3 INTEGER,\n" +
+                "v1 VARCHAR,\n" +
+                "CONSTRAINT pk PRIMARY KEY (t_id, k1, k2))\n"
+                        + (saltBuckets != null && splits == null ? (",salt_buckets=" + saltBuckets) : "" 
+                        + (saltBuckets == null && splits != null ? (" split on " + splits) : ""));
+        conn.createStatement().execute(ddl);
+        conn.close();
+    }
+    
+    @Test
+    public void testLocalIndexRoundTrip() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, null);
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+        conn1.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+        PTable localIndex = conn1.unwrap(PhoenixConnection.class).getMetaDataCache().getTable(new PTableKey(null,INDEX_TABLE_NAME));
+        assertEquals(IndexType.LOCAL, localIndex.getIndexType());
+        assertNotNull(localIndex.getViewIndexId());
+    }
+    
+    @Test
+    public void testLocalIndexCreationWithSplitsShouldFail() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, null);
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        try {
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)"+" split on (1,2,3)");
+            fail("Local index cannot be pre-split");
+        } catch (SQLException e) { }
+        try {
+            conn2.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+            conn2.unwrap(PhoenixConnection.class).getMetaDataCache().getTable(new PTableKey(null,INDEX_TABLE_NAME));
+            fail("Local index should not be created.");
+        } catch (TableNotFoundException e) { }
+    }
+
+    @Test
+    public void testLocalIndexCreationWithSaltingShouldFail() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, null);
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        try {
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)"+" salt_buckets=16");
+            fail("Local index cannot be salted.");
+        } catch (SQLException e) { }
+        try {
+            conn2.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+            conn2.unwrap(PhoenixConnection.class).getMetaDataCache().getTable(new PTableKey(null,INDEX_TABLE_NAME));
+            fail("Local index should not be created.");
+        } catch (TableNotFoundException e) { }
+    }
+
+    @Test
+    public void testLocalIndexOnTableWithImmutableRows() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, null);
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        try {
+            conn1.createStatement().execute("ALTER TABLE " + DATA_TABLE_NAME + " SET IMMUTABLE_ROWS=true");
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+            fail("Local index aren't allowed on table with immutable rows");
+        } catch (SQLException e) { }
+        try {
+            conn2.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+            conn2.unwrap(PhoenixConnection.class).getMetaDataCache().getTable(new PTableKey(null,INDEX_TABLE_NAME));
+            fail("Local index should not be created.");
+        } catch (TableNotFoundException e) { }
+    }
+
+    @Test
+    public void testLocalIndexTableRegionSplitPolicyAndSplitKeys() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null,"('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+        conn2.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        HTableDescriptor htd = admin.getTableDescriptor(TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)));
+        assertEquals(IndexRegionSplitPolicy.class.getName(), htd.getValue(HTableDescriptor.SPLIT_POLICY));
+        HTable userTable = new HTable(admin.getConfiguration(),TableName.valueOf(DATA_TABLE_NAME));
+        HTable indexTable = new HTable(admin.getConfiguration(),TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)));
+        assertEquals("Both user region and index table should have same split keys.", userTable.getStartKeys(), indexTable.getStartKeys());
+    }
+
+    @Test
+    public void testDropLocalIndexTable() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, null);
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+        conn2.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        assertTrue("Local index table should be present.", admin.tableExists(TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME))));
+        conn1.createStatement().execute("DROP TABLE "+ DATA_TABLE_NAME);
+        admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        assertFalse("Local index table should be deleted.", admin.tableExists(TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME))));
+        ResultSet rs = conn2.createStatement().executeQuery("SELECT "
+                + PhoenixDatabaseMetaData.SEQUENCE_SCHEMA + ","
+                + PhoenixDatabaseMetaData.SEQUENCE_NAME
+                + " FROM " + PhoenixDatabaseMetaData.SEQUENCE_TABLE_NAME);
+        assertFalse("View index sequences should be deleted.", rs.next());
+    }
+    
+    @Test
+    public void testPutsToLocalIndexTable() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('b',1,2,4,'z')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('f',1,2,3,'z')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('j',2,4,2,'a')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('q',3,1,1,'c')");
+        conn1.commit();
+        ResultSet rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + INDEX_TABLE_NAME);
+        assertTrue(rs.next());
+        assertEquals(4, rs.getInt(1));
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        HTable indexTable = new HTable(admin.getConfiguration() ,TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)));
+        Pair<byte[][], byte[][]> startEndKeys = indexTable.getStartEndKeys();
+        byte[][] startKeys = startEndKeys.getFirst();
+        byte[][] endKeys = startEndKeys.getSecond();
+        for (int i = 0; i < startKeys.length; i++) {
+            Scan s = new Scan();
+            s.setStartRow(startKeys[i]);
+            s.setStopRow(endKeys[i]);
+            ResultScanner scanner = indexTable.getScanner(s);
+            int count = 0;
+            for(Result r:scanner){
+                count++;
+            }
+            scanner.close();
+            assertEquals(1, count);
+        }
+        indexTable.close();
+    }
+    
+    @Test
+    public void testBuildIndexWhenUserTableAlreadyHasData() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('b',1,2,4,'z')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('f',1,2,3,'z')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('j',2,4,2,'a')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('q',3,1,1,'c')");
+        conn1.commit();
+        conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+        ResultSet rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + INDEX_TABLE_NAME);
+        assertTrue(rs.next());
+        assertEquals(4, rs.getInt(1));
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        HTable indexTable = new HTable(admin.getConfiguration() ,TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)));
+        Pair<byte[][], byte[][]> startEndKeys = indexTable.getStartEndKeys();
+        byte[][] startKeys = startEndKeys.getFirst();
+        byte[][] endKeys = startEndKeys.getSecond();
+        for (int i = 0; i < startKeys.length; i++) {
+            Scan s = new Scan();
+            s.setStartRow(startKeys[i]);
+            s.setStopRow(endKeys[i]);
+            ResultScanner scanner = indexTable.getScanner(s);
+            int count = 0;
+            for(Result r:scanner){
+                count++;
+            }
+            scanner.close();
+            assertEquals(1, count);
+        }
+        indexTable.close();
+    }
+
+    @Test
+    public void testLocalIndexScan() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        try{
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('a',1,2,5,'y')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('b',1,2,4,'z')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('f',1,2,3,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('e',1,2,3,'b')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('j',2,4,2,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('q',3,1,1,'c')");
+            conn1.commit();
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+            
+            ResultSet rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + INDEX_TABLE_NAME);
+            assertTrue(rs.next());
+            
+            HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+            int numRegions = admin.getTableRegions(TableName.valueOf(DATA_TABLE_NAME)).size();
+            
+            String query = "SELECT t_id, k1, k2,V1 FROM " + DATA_TABLE_NAME +" where v1='a'";
+            rs = conn1.createStatement().executeQuery("EXPLAIN "+ query);
+            
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER "
+                        + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME) + " [-32768,'a']\nCLIENT MERGE SORT",
+                        QueryUtil.getExplainPlan(rs));
+            
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("f", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertTrue(rs.next());
+            assertEquals("j", rs.getString("t_id"));
+            assertEquals(2, rs.getInt("k1"));
+            assertEquals(4, rs.getInt("k2"));
+            assertFalse(rs.next());
+            query = "SELECT t_id, k1, k2,V1, k3 FROM " + DATA_TABLE_NAME +" where v1<='z' order by k3";
+            rs = conn1.createStatement().executeQuery("EXPLAIN "+ query);
+            
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER " + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME) + " [-32768,*] - [-32768,'z']\n" + 
+                "    SERVER SORTED BY [K3]\n" +
+                "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+ 
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals(1, rs.getInt("k3"));
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt("k3"));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt("k3"));
+            assertTrue(rs.next());
+            assertEquals(3, rs.getInt("k3"));
+            assertTrue(rs.next());
+            assertEquals(4, rs.getInt("k3"));
+            assertTrue(rs.next());
+            assertEquals(5, rs.getInt("k3"));
+            assertFalse(rs.next());
+            
+            query = "SELECT t_id, k1, k2,v1 from " + DATA_TABLE_FULL_NAME + " order by V1,t_id";
+            rs = conn1.createStatement().executeQuery("EXPLAIN " + query);
+            
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY FULL SCAN OVER "
+                        + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)+"\nCLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+            
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("f", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals("a", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("j", rs.getString("t_id"));
+            assertEquals(2, rs.getInt("k1"));
+            assertEquals(4, rs.getInt("k2"));
+            assertEquals("a", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("e", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals("b", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("q", rs.getString("t_id"));
+            assertEquals(3, rs.getInt("k1"));
+            assertEquals(1, rs.getInt("k2"));
+            assertEquals("c", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("a", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals("y", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("b", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals("z", rs.getString("V1"));
+        } finally {
+            conn1.close();
+        }
+    }
+
+    @Test
+    public void testLocalIndexScanJoinColumnsFromDataTable() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        try{
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('b',1,2,4,'z')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('f',1,2,3,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('j',2,4,2,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('q',3,1,1,'c')");
+            conn1.commit();
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+            
+            ResultSet rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + INDEX_TABLE_NAME);
+            assertTrue(rs.next());
+            
+            HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+            int numRegions = admin.getTableRegions(TableName.valueOf(DATA_TABLE_NAME)).size();
+            
+            String query = "SELECT t_id, k1, k2, k3, V1 FROM " + DATA_TABLE_NAME +" where v1='a'";
+            rs = conn1.createStatement().executeQuery("EXPLAIN "+ query);
+            
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER "
+                        + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME) + " [-32768,'a']\nCLIENT MERGE SORT",
+                        QueryUtil.getExplainPlan(rs));
+            
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("f", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals(3, rs.getInt("k3"));
+            assertTrue(rs.next());
+            assertEquals("j", rs.getString("t_id"));
+            assertEquals(2, rs.getInt("k1"));
+            assertEquals(4, rs.getInt("k2"));
+            assertEquals(2, rs.getInt("k3"));
+            assertFalse(rs.next());
+            
+            query = "SELECT t_id, k1, k2, k3, V1 from " + DATA_TABLE_FULL_NAME + "  where v1<='z' order by V1,t_id";
+            rs = conn1.createStatement().executeQuery("EXPLAIN " + query);
+            
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER "
+                        + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)+" [-32768,*] - [-32768,'z']\nCLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+            
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("f", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals(3, rs.getInt("k3"));
+            assertEquals("a", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("j", rs.getString("t_id"));
+            assertEquals(2, rs.getInt("k1"));
+            assertEquals(4, rs.getInt("k2"));
+            assertEquals(2, rs.getInt("k3"));
+            assertEquals("a", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("q", rs.getString("t_id"));
+            assertEquals(3, rs.getInt("k1"));
+            assertEquals(1, rs.getInt("k2"));
+            assertEquals(1, rs.getInt("k3"));
+            assertEquals("c", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("b", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k1"));
+            assertEquals(2, rs.getInt("k2"));
+            assertEquals(4, rs.getInt("k3"));
+            assertEquals("z", rs.getString("V1"));
+            
+            query = "SELECT t_id, V1, k3 from " + DATA_TABLE_FULL_NAME + "  where v1 <='z' group by v1,t_id, k3";
+            rs = conn1.createStatement().executeQuery("EXPLAIN " + query);
+            
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER "
+                        + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)+" [-32768,*] - [-32768,'z']\n"
+                        + "    SERVER AGGREGATE INTO DISTINCT ROWS BY [V1, T_ID, K3]\n" + "CLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+            
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("f", rs.getString("t_id"));
+            assertEquals(3, rs.getInt("k3"));
+            assertEquals("a", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("j", rs.getString("t_id"));
+            assertEquals(2, rs.getInt("k3"));
+            assertEquals("a", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("q", rs.getString("t_id"));
+            assertEquals(1, rs.getInt("k3"));
+            assertEquals("c", rs.getString("V1"));
+            assertTrue(rs.next());
+            assertEquals("b", rs.getString("t_id"));
+            assertEquals(4, rs.getInt("k3"));
+            assertEquals("z", rs.getString("V1"));
+            
+            query = "SELECT v1,sum(k3) from " + DATA_TABLE_FULL_NAME + " where v1 <='z'  group by v1 order by v1";
+            PhoenixPreparedStatement statement = conn1.prepareStatement(query).unwrap(PhoenixPreparedStatement.class);
+            QueryPlan plan = statement.compileQuery("EXPLAIN " + query);
+            assertTrue(query, plan.getContext().getScan().getAttribute(BaseScannerRegionObserver.KEY_ORDERED_GROUP_BY_EXPRESSIONS) == null);
+            assertTrue(query, plan.getContext().getScan().getAttribute(BaseScannerRegionObserver.UNORDERED_GROUP_BY_EXPRESSIONS) != null);
+            
+            rs = conn1.createStatement().executeQuery("EXPLAIN " + query);
+            assertEquals(
+                "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER "
+                        + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)+" [-32768,*] - [-32768,'z']\n"
+                        + "    SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [V1]\nCLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+            
+            rs = conn1.createStatement().executeQuery(query);
+            assertTrue(rs.next());
+            assertEquals("a", rs.getString(1));
+            assertEquals(5, rs.getInt(2));
+            assertTrue(rs.next());
+            assertEquals("c", rs.getString(1));
+            assertEquals(1, rs.getInt(2));
+            assertTrue(rs.next());
+            assertEquals("z", rs.getString(1));
+            assertEquals(4, rs.getInt(2));
+       } finally {
+            conn1.close();
+        }
+    }
+
+    @Test
+    public void testIndexPlanSelectionIfBothGlobalAndLocalIndexesHasSameColumnsAndOrder() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('b',1,2,4,'z')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('f',1,2,3,'a')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('j',2,4,3,'a')");
+        conn1.createStatement().execute("UPSERT INTO "+DATA_TABLE_NAME+" values('q',3,1,1,'c')");
+        conn1.commit();
+        conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+        conn1.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + "2" + " ON " + DATA_TABLE_NAME + "(v1)");
+        String query = "SELECT t_id, k1, k2,V1 FROM " + DATA_TABLE_NAME +" where v1='a'";
+        ResultSet rs1 = conn1.createStatement().executeQuery("EXPLAIN "+ query);
+        assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + INDEX_TABLE_NAME + "2" + " ['a']",QueryUtil.getExplainPlan(rs1));
+        conn1.close();
+    }
+
+    @Test
+    public void testDropLocalIndexShouldDeleteDataFromLocalIndexTable() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        try {
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('b',1,2,4,'z')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('f',1,2,3,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('j',2,4,2,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('q',3,1,1,'c')");
+            conn1.commit();
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+            conn1.createStatement().execute("DROP INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME);
+            HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+            HTable indexTable = new HTable(admin.getConfiguration() ,TableName.valueOf(MetaDataUtil.getLocalIndexTableName(DATA_TABLE_NAME)));
+            Pair<byte[][], byte[][]> startEndKeys = indexTable.getStartEndKeys();
+            byte[][] startKeys = startEndKeys.getFirst();
+            byte[][] endKeys = startEndKeys.getSecond();
+            // No entry should be present in local index table after drop index.
+            for (int i = 0; i < startKeys.length; i++) {
+                Scan s = new Scan();
+                s.setStartRow(startKeys[i]);
+                s.setStopRow(endKeys[i]);
+                ResultScanner scanner = indexTable.getScanner(s);
+                int count = 0;
+                for(Result r:scanner){
+                    count++;
+                }
+                scanner.close();
+                assertEquals(0, count);
+            }
+            indexTable.close();
+        } finally {
+            conn1.close();
+        }
+    }
+
+    @Test
+    public void testLocalIndexRowsShouldBeDeletedWhenUserTableRowsDeleted() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, "('e','i','o')");
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        try {
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('b',1,2,4,'z')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('f',1,2,3,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('j',2,4,2,'a')");
+            conn1.createStatement().execute("UPSERT INTO " + DATA_TABLE_NAME + " values('q',3,1,1,'c')");
+            conn1.commit();
+            conn1.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_NAME + "(v1)");
+            conn1.createStatement().execute("DELETE FROM " + DATA_TABLE_NAME + " where v1='a'");
+            conn1.commit();
+            conn1 = DriverManager.getConnection(getUrl());
+            ResultSet rs = conn1.createStatement().executeQuery("SELECT COUNT(*) FROM " + INDEX_TABLE_NAME);
+            assertTrue(rs.next());
+            assertEquals(2, rs.getInt(1));
+        } finally {
+            conn1.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/247ac8ee/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexIT.java
index fed948b..4db967a 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexIT.java
@@ -38,6 +38,7 @@ import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
 import org.apache.phoenix.end2end.HBaseManagedTimeTest;
 import org.apache.phoenix.end2end.Shadower;
 import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.TestUtil;
@@ -59,28 +60,49 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         props.put(QueryServices.MAX_INTRA_REGION_PARALLELIZATION_ATTRIB, Integer.toString(1));
         // Forces server cache to be used
         props.put(QueryServices.INDEX_MUTATE_BATCH_SIZE_THRESHOLD_ATTRIB, Integer.toString(2));
+        props.put(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
         // Must update config before starting server
         props.put(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
         setUpTestDriver(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
     }
-    
+
     @Test
     public void testIndexWithNullableFixedWithCols() throws Exception {
+        testIndexWithNullableFixedWithCols(false);
+    }
+    
+    @Test
+    public void testLocalIndexWithNullableFixedWithCols() throws Exception {
+        testIndexWithNullableFixedWithCols(true);
+    }
+
+    private void testIndexWithNullableFixedWithCols(boolean localIndex) throws Exception {
         Properties props = new Properties(TEST_PROPERTIES);
         Connection conn = DriverManager.getConnection(getUrl(), props);
         conn.setAutoCommit(false);
         try {
             createTestTable();
             populateTestTable();
-            String ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME
-                    + " (char_col1 ASC, int_col1 ASC)"
-                    + " INCLUDE (long_col1, long_col2)";
+            String ddl = null;
+            if(localIndex){
+                ddl = "CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME
+                        + " (char_col1 ASC, int_col1 ASC)"
+                        + " INCLUDE (long_col1, long_col2)";
+            } else {
+                ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME
+                        + " (char_col1 ASC, int_col1 ASC)"
+                        + " INCLUDE (long_col1, long_col2)";
+            }
             PreparedStatement stmt = conn.prepareStatement(ddl);
             stmt.execute();
             
             String query = "SELECT d.char_col1, int_col1 from " + DATA_TABLE_FULL_NAME + " as d";
             ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + MetaDataUtil.getLocalIndexTableName(DATA_TABLE_FULL_NAME)+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            }
             
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());
@@ -101,6 +123,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
     @Test
     public void testIndexWithNullableDateCol() throws Exception {
+        testIndexWithNullableDateCol(false);
+    }
+
+    @Test
+    public void testLocalIndexWithNullableDateCol() throws Exception {
+        testIndexWithNullableDateCol(true);
+    }
+
+    private void testIndexWithNullableDateCol(boolean localIndex) throws Exception {
         Properties props = new Properties(TEST_PROPERTIES);
         Connection conn = DriverManager.getConnection(getUrl(), props);
         conn.setAutoCommit(false);
@@ -109,13 +140,22 @@ public class MutableIndexIT extends BaseMutableIndexIT {
             
             createTestTable();
             populateTestTable(date);
-            String ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (date_col)";
+            String ddl = null;
+            if (localIndex) {
+                ddl = "CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (date_col)";
+            } else {
+                ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (date_col)";
+            }
             PreparedStatement stmt = conn.prepareStatement(ddl);
             stmt.execute();
             
             String query = "SELECT int_pk from " + DATA_TABLE_FULL_NAME ;
             ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME +"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            }
             
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());
@@ -128,7 +168,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
             
             query = "SELECT date_col from " + DATA_TABLE_FULL_NAME + " order by date_col" ;
             rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME + "\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            }
             
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());
@@ -145,21 +189,42 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
     @Test
     public void testCoveredColumnUpdates() throws Exception {
+        testCoveredColumnUpdates(false);
+    }
+
+    @Test
+    public void testCoveredColumnUpdatesWithLocalIndex() throws Exception {
+        testCoveredColumnUpdates(true);
+    }
+
+    private void testCoveredColumnUpdates(boolean localIndex) throws Exception {
         Properties props = new Properties(TEST_PROPERTIES);
         Connection conn = DriverManager.getConnection(getUrl(), props);
         conn.setAutoCommit(false);
         try {
             createTestTable();
             populateTestTable();
-            String ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME
-                    + " (char_col1 ASC, int_col1 ASC)"
-                    + " INCLUDE (long_col1, long_col2)";
+            String ddl = null;
+            if(localIndex) {
+                ddl = "CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME
+                        + " (char_col1 ASC, int_col1 ASC)"
+                        + " INCLUDE (long_col1, long_col2)";
+            } else {
+                ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME
+                        + " (char_col1 ASC, int_col1 ASC)"
+                        + " INCLUDE (long_col1, long_col2)";
+            }
+
             PreparedStatement stmt = conn.prepareStatement(ddl);
             stmt.execute();
             
             String query = "SELECT char_col1, int_col1, long_col2 from " + DATA_TABLE_FULL_NAME;
             ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            if (localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME +"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            }
             
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());
@@ -228,6 +293,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
     
     @Test
     public void testSelectAllAndAliasWithIndex() throws Exception {
+        testSelectAllAndAliasWithIndex(false);
+    }
+
+    @Test
+    public void testSelectAllAndAliasWithLocalIndex() throws Exception {
+        testSelectAllAndAliasWithIndex(true);
+    }
+
+    private void testSelectAllAndAliasWithIndex(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
         
@@ -239,7 +313,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
         
-        conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v2 DESC) INCLUDE (v1)");
+        if (localIndex) {
+            conn.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v2 DESC) INCLUDE (v1)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v2 DESC) INCLUDE (v1)");
+        }
         query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
@@ -257,7 +335,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        if(localIndex){
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        }
 
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
@@ -278,9 +360,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         
         query = "SELECT v1 as foo FROM " + DATA_TABLE_FULL_NAME + " WHERE v2 = '1' ORDER BY foo";
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +INDEX_TABLE_FULL_NAME + " [~'1']\n" + 
-                "    SERVER SORTED BY [V1]\n" + 
-                "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        if(localIndex){
+            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_" +DATA_TABLE_FULL_NAME + " [-32768,~'1']\n" + 
+                    "    SERVER SORTED BY [V1]\n" + 
+                    "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +INDEX_TABLE_FULL_NAME + " [~'1']\n" + 
+                    "    SERVER SORTED BY [V1]\n" + 
+                    "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        }
 
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
@@ -291,6 +379,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
     
     @Test
     public void testSelectCF() throws Exception {
+        testSelectCF(false);
+    }
+
+    @Test
+    public void testSelectCFWithLocalIndex() throws Exception {
+        testSelectCF(true);
+    }
+
+    private void testSelectCF(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
         
@@ -301,8 +398,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
-        
-        conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v2 DESC) INCLUDE (a.v1)");
+        if(localIndex) {
+            conn.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v2 DESC) INCLUDE (a.v1)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v2 DESC) INCLUDE (a.v1)");
+        }
         query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
@@ -326,7 +426,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
         query = "SELECT a.* FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        if(localIndex) {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        }
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
         assertEquals("y",rs.getString(1));
@@ -343,6 +447,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
     
     @Test
     public void testCoveredColumns() throws Exception {
+        testCoveredColumns(false);
+    }
+
+    @Test
+    public void testCoveredColumnsWithLocalIndex() throws Exception {
+        testCoveredColumns(true);
+    }
+
+    private void testCoveredColumns(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
         
@@ -354,7 +467,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
         
-        conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) INCLUDE (v2)");
+        if(localIndex) {
+            conn.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) INCLUDE (v2)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) INCLUDE (v2)");
+        }
         query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
@@ -390,7 +507,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        if(localIndex) {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));            
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        }
 
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
@@ -407,8 +528,12 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
-
+        if(localIndex) {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME + "\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));            
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        }
+        
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
         assertEquals("a",rs.getString(1));
@@ -424,8 +549,12 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
-
+        if(localIndex) {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));            
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        }
+        
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
         assertEquals("a",rs.getString(1));
@@ -436,6 +565,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
     @Test
     public void testCompoundIndexKey() throws Exception {
+        testCompoundIndexKey(false);
+    }
+
+    @Test
+    public void testCompoundIndexKeyWithLocalIndex() throws Exception {
+        testCompoundIndexKey(true);
+    }
+
+    private void testCompoundIndexKey(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
         
@@ -448,8 +586,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
-        
-        conn.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+        if(localIndex) {
+            conn.createStatement().execute("CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+        } else {
+            conn.createStatement().execute("CREATE INDEX " + 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());
@@ -487,7 +628,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        if (localIndex) {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+        }
         //make sure the data table looks like what we expect
         rs = conn.createStatement().executeQuery(query);
         assertTrue(rs.next());
@@ -536,6 +681,16 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
     }
  
+    @Test
+    public void testMultipleUpdatesToSingleRow() throws Exception {
+        testMultipleUpdatesToSingleRow(false);
+    }
+
+    @Test
+    public void testMultipleUpdatesToSingleRowWithLocalIndex() throws Exception {
+        testMultipleUpdatesToSingleRow(true);
+    }
+    
     /**
      * There was a case where if there were multiple updates to a single row in the same batch, the
      * index wouldn't be updated correctly as each element of the batch was evaluated with the state
@@ -544,8 +699,7 @@ public class MutableIndexIT extends BaseMutableIndexIT {
      * and current + delete, but not current + put + delete.
      * @throws Exception on failure
      */
-    @Test
-    public void testMultipleUpdatesToSingleRow() throws Exception {
+    private void testMultipleUpdatesToSingleRow(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
     
@@ -560,9 +714,14 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
-    
-        conn.createStatement().execute(
-          "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+
+        if(localIndex) {
+            conn.createStatement().execute(
+                "CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+        } else {
+            conn.createStatement().execute(
+                "CREATE INDEX " + 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());
@@ -606,8 +765,13 @@ public class MutableIndexIT extends BaseMutableIndexIT {
     
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME,
-          QueryUtil.getExplainPlan(rs));
+        if(localIndex) {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME,
+                QueryUtil.getExplainPlan(rs));
+        }
     
         // check that the data table matches as expected
         rs = conn.createStatement().executeQuery(query);
@@ -681,6 +845,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
     
     @Test
     public void testMultipleUpdatesAcrossRegions() throws Exception {
+        testMultipleUpdatesAcrossRegions(false);
+    }
+
+    @Test
+    public void testMultipleUpdatesAcrossRegionsWithLocalIndex() throws Exception {
+        testMultipleUpdatesAcrossRegions(true);
+    }
+
+    private void testMultipleUpdatesAcrossRegions(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
     
@@ -696,9 +869,14 @@ public class MutableIndexIT extends BaseMutableIndexIT {
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery(query);
         assertFalse(rs.next());
-    
-        conn.createStatement().execute(
-          "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+
+        if(localIndex) {
+            conn.createStatement().execute(
+                "CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)");
+        } else {
+            conn.createStatement().execute(
+                "CREATE INDEX " + 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());
@@ -739,8 +917,13 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
         query = "SELECT * FROM " + DATA_TABLE_FULL_NAME;
         rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME,
-          QueryUtil.getExplainPlan(rs));
+        if (localIndex) {
+            assertEquals("CLIENT PARALLEL 2-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT",
+                QueryUtil.getExplainPlan(rs));
+        } else {
+            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME,
+                QueryUtil.getExplainPlan(rs));
+        }
     
         // check that the data table matches as expected
         rs = conn.createStatement().executeQuery(query);
@@ -761,6 +944,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
     
     @Test
     public void testIndexWithCaseSensitiveCols() throws Exception {
+        testIndexWithCaseSensitiveCols(false);
+    }
+
+    @Test
+    public void testLocalIndexWithCaseSensitiveCols() throws Exception {
+        testIndexWithCaseSensitiveCols(true);
+    }
+
+    private void testIndexWithCaseSensitiveCols(boolean localIndex) throws Exception {
         String query;
         ResultSet rs;
         
@@ -772,8 +964,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
             query = "SELECT * FROM cs";
             rs = conn.createStatement().executeQuery(query);
             assertFalse(rs.next());
-
-            conn.createStatement().execute("CREATE INDEX ics ON cs (\"v2\") INCLUDE (\"V1\")");
+            if (localIndex) {
+                conn.createStatement().execute("CREATE LOCAL INDEX ics ON cs (\"v2\") INCLUDE (\"V1\")");
+            } else {
+                conn.createStatement().execute("CREATE INDEX ics ON cs (\"v2\") INCLUDE (\"V1\")");
+            }
             query = "SELECT * FROM ics";
             rs = conn.createStatement().executeQuery(query);
             assertFalse(rs.next());
@@ -791,7 +986,11 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
             query = "SELECT * FROM cs WHERE \"v2\" = '1'";
             rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER ICS ['1']", QueryUtil.getExplainPlan(rs));
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_CS [-32768,'1']\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER ICS ['1']", QueryUtil.getExplainPlan(rs));
+            }
 
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());
@@ -805,7 +1004,12 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
             query = "SELECT \"V1\", \"V1\" as foo1, \"v2\" as foo, \"v2\" as \"Foo1\", \"v2\" FROM cs ORDER BY foo";
             rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER ICS", QueryUtil.getExplainPlan(rs));
+            if(localIndex){
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_CS\nCLIENT MERGE SORT",
+                    QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER ICS", QueryUtil.getExplainPlan(rs));
+            }
 
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());
@@ -838,13 +1042,26 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
     @Test
     public void testInFilterOnIndexedTable() throws Exception {
+        testInFilterOnIndexedTable(false);
+    }
+    
+    @Test
+    public void testInFilterOnLocalIndexedTable() throws Exception {
+        testInFilterOnIndexedTable(true);
+    }
+    
+    private void testInFilterOnIndexedTable(boolean localIndex) throws Exception {
         Properties props = new Properties(TEST_PROPERTIES);
         Connection conn = DriverManager.getConnection(getUrl(), props);
         conn.setAutoCommit(false);
         try {
 	        String ddl = "CREATE TABLE TEST (PK1 CHAR(2) NOT NULL PRIMARY KEY, CF1.COL1 BIGINT)";
 	        conn.createStatement().execute(ddl);
-	        ddl = "CREATE INDEX IDX1 ON TEST (COL1)";
+	        if(localIndex) {
+	            ddl = "CREATE LOCAL INDEX IDX1 ON TEST (COL1)";
+	        } else {
+	            ddl = "CREATE INDEX IDX1 ON TEST (COL1)";
+	        }
 	        conn.createStatement().execute(ddl);
 	
 	        String query = "SELECT COUNT(COL1) FROM TEST WHERE COL1 IN (1,25,50,75,100)"; 
@@ -857,6 +1074,15 @@ public class MutableIndexIT extends BaseMutableIndexIT {
 
     @Test
     public void testIndexWithDecimalCol() throws Exception {
+        testIndexWithDecimalCol(false);
+    }
+
+    @Test
+    public void testLocalIndexWithDecimalCol() throws Exception {
+        testIndexWithDecimalCol(true);
+    }
+
+    private void testIndexWithDecimalCol(boolean localIndex) throws Exception {
         Properties props = new Properties(TEST_PROPERTIES);
         Connection conn = DriverManager.getConnection(getUrl(), props);
         conn.setAutoCommit(false);
@@ -865,13 +1091,22 @@ public class MutableIndexIT extends BaseMutableIndexIT {
             
             createTestTable();
             populateTestTable(date);
-            String ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (decimal_pk) INCLUDE (decimal_col1, decimal_col2)";
+            String ddl = null;
+            if (localIndex) {
+                ddl = "CREATE LOCAL INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (decimal_pk) INCLUDE (decimal_col1, decimal_col2)";
+            } else {
+                ddl = "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (decimal_pk) INCLUDE (decimal_col1, decimal_col2)";
+            }
             PreparedStatement stmt = conn.prepareStatement(ddl);
             stmt.execute();
             
             String query = "SELECT decimal_pk, decimal_col1, decimal_col2 from " + DATA_TABLE_FULL_NAME ;
             ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
-            assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            if(localIndex) {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER _LOCAL_IDX_" + DATA_TABLE_FULL_NAME+"\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs));
+            } else {
+                assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + INDEX_TABLE_FULL_NAME, QueryUtil.getExplainPlan(rs));
+            }
             
             rs = conn.createStatement().executeQuery(query);
             assertTrue(rs.next());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/247ac8ee/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ViewIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ViewIndexIT.java
new file mode 100644
index 0000000..cad56d0
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ViewIndexIT.java
@@ -0,0 +1,85 @@
+/*
+ * 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.end2end.index;
+
+import static org.junit.Assert.assertFalse;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.MetaDataUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+
+public class ViewIndexIT extends BaseIndexIT {
+
+    private String VIEW_NAME = "MY_VIEW";
+
+    @BeforeClass
+    public static void doSetup() throws Exception {
+        Map<String,String> props = Maps.newHashMapWithExpectedSize(3);
+        // Drop the HBase table metadata for this test
+        props.put(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
+        // Must update config before starting server
+        setUpTestDriver(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+    private void createBaseTable(String tableName, Integer saltBuckets, String splits) throws SQLException {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE " + tableName + " (t_id VARCHAR NOT NULL,\n" +
+                "k1 INTEGER NOT NULL,\n" +
+                "k2 INTEGER NOT NULL,\n" +
+                "v1 VARCHAR,\n" +
+                "CONSTRAINT pk PRIMARY KEY (t_id, k1, k2))\n"
+                        + (saltBuckets == null || splits != null ? "" : (",salt_buckets=" + saltBuckets)
+                        + (saltBuckets != null || splits == null ? "" : ",splits=" + splits));
+        conn.createStatement().execute(ddl);
+        conn.close();
+    }
+
+    @Test
+    public void testDeleteViewIndexSequences() throws Exception {
+        createBaseTable(DATA_TABLE_NAME, null, null);
+        Connection conn1 = DriverManager.getConnection(getUrl());
+        Connection conn2 = DriverManager.getConnection(getUrl());
+        conn1.createStatement().execute("CREATE VIEW " + VIEW_NAME + " AS SELECT * FROM " + DATA_TABLE_NAME);
+        conn1.createStatement().execute("CREATE INDEX " + INDEX_TABLE_NAME + " ON " + VIEW_NAME + " (v1)");
+        conn2.createStatement().executeQuery("SELECT * FROM " + DATA_TABLE_FULL_NAME).next();
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        conn1.createStatement().execute("DROP VIEW " + VIEW_NAME);
+        conn1.createStatement().execute("DROP TABLE "+ DATA_TABLE_NAME);
+        admin = driver.getConnectionQueryServices(getUrl(), TestUtil.TEST_PROPERTIES).getAdmin();
+        assertFalse("View index table should be deleted.", admin.tableExists(TableName.valueOf(MetaDataUtil.getViewIndexTableName(DATA_TABLE_NAME))));
+        ResultSet rs = conn2.createStatement().executeQuery("SELECT "
+                + PhoenixDatabaseMetaData.SEQUENCE_SCHEMA + ","
+                + PhoenixDatabaseMetaData.SEQUENCE_NAME
+                + " FROM " + PhoenixDatabaseMetaData.SEQUENCE_TABLE_NAME);
+        assertFalse("View index sequences should be deleted.", rs.next());
+    }
+}
\ No newline at end of file