You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2014/03/12 22:31:51 UTC

[24/41] PHOENIX-130 Separate execution of slow (integration) tests from fast unit tests (Gabriel Reid)

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/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
new file mode 100644
index 0000000..e1080e1
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
@@ -0,0 +1,220 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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 org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.schema.ReadOnlyTableException;
+import org.junit.Test;
+
+public class ViewIT extends BaseViewIT {
+    
+    @Test
+    public void testReadOnlyView() throws Exception {
+        Connection earlierCon = DriverManager.getConnection(getUrl());
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE t (k INTEGER NOT NULL PRIMARY KEY, v1 DATE)";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW v (v2 VARCHAR) AS SELECT * FROM t WHERE k > 5";
+        conn.createStatement().execute(ddl);
+        try {
+            conn.createStatement().execute("UPSERT INTO v VALUES(1)");
+            fail();
+        } catch (ReadOnlyTableException e) {
+            
+        }
+        for (int i = 0; i < 10; i++) {
+            conn.createStatement().execute("UPSERT INTO t VALUES(" + i + ")");
+        }
+        conn.commit();
+        
+        int count = 0;
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k FROM v");
+        while (rs.next()) {
+            count++;
+            assertEquals(count + 5, rs.getInt(1));
+        }
+        assertEquals(4, count);
+        count = 0;
+        rs = earlierCon.createStatement().executeQuery("SELECT k FROM v");
+        while (rs.next()) {
+            count++;
+            assertEquals(count + 5, rs.getInt(1));
+        }
+        assertEquals(4, count);
+    }
+
+
+    @Test
+    public void testReadOnlyOnReadOnlyView() throws Exception {
+        testReadOnlyView();
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE VIEW v2 AS SELECT * FROM v WHERE k < 9";
+        conn.createStatement().execute(ddl);
+        try {
+            conn.createStatement().execute("UPSERT INTO v2 VALUES(1)");
+            fail();
+        } catch (ReadOnlyTableException e) {
+            
+        }
+
+        int count = 0;
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k FROM v2");
+        while (rs.next()) {
+            count++;
+            assertEquals(count + 5, rs.getInt(1));
+        }
+        assertEquals(3, count);
+    }
+
+    @Test
+    public void testNonSaltedUpdatableViewWithIndex() throws Exception {
+        testUpdatableViewWithIndex(null);
+    }
+    
+    @Test
+    public void testUpdatableOnUpdatableView() throws Exception {
+        testUpdatableView(null);
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE VIEW v2 AS SELECT * FROM v WHERE k3 = 2";
+        conn.createStatement().execute(ddl);
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k1, k2, k3 FROM v2");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertEquals(109, rs.getInt(2));
+        assertEquals(2, rs.getInt(3));
+        assertFalse(rs.next());
+
+        conn.createStatement().execute("UPSERT INTO v2(k2) VALUES(122)");
+        conn.commit();
+        rs = conn.createStatement().executeQuery("SELECT k1, k2, k3 FROM v2 WHERE k2 >= 120");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertEquals(122, rs.getInt(2));
+        assertEquals(2, rs.getInt(3));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testReadOnlyOnUpdatableView() throws Exception {
+        testUpdatableView(null);
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE VIEW v2 AS SELECT * FROM v WHERE k3 > 1 and k3 < 50";
+        conn.createStatement().execute(ddl);
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k1, k2, k3 FROM v2");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertEquals(109, rs.getInt(2));
+        assertEquals(2, rs.getInt(3));
+        assertFalse(rs.next());
+
+        try {
+            conn.createStatement().execute("UPSERT INTO v2 VALUES(1)");
+            fail();
+        } catch (ReadOnlyTableException e) {
+            
+        }
+        
+        conn.createStatement().execute("UPSERT INTO t(k1, k2,k3) VALUES(1, 122, 5)");
+        conn.commit();
+        rs = conn.createStatement().executeQuery("SELECT k1, k2, k3 FROM v2 WHERE k2 >= 120");
+        assertTrue(rs.next());
+        assertEquals(1, rs.getInt(1));
+        assertEquals(122, rs.getInt(2));
+        assertEquals(5, rs.getInt(3));
+        assertFalse(rs.next());
+    }
+    
+    @Test
+    public void testDisallowDropOfReferencedColumn() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE t (k1 INTEGER NOT NULL, k2 INTEGER NOT NULL, v1 DECIMAL, CONSTRAINT pk PRIMARY KEY (k1, k2))";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW v1(v2 VARCHAR, v3 VARCHAR) AS SELECT * FROM t WHERE v1 = 1.0";
+        conn.createStatement().execute(ddl);
+        
+        try {
+            conn.createStatement().execute("ALTER VIEW v1 DROP COLUMN v1");
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+        }
+        
+        ddl = "CREATE VIEW v2 AS SELECT * FROM v1 WHERE v2 != 'foo'";
+        conn.createStatement().execute(ddl);
+
+        try {
+            conn.createStatement().execute("ALTER VIEW v2 DROP COLUMN v1");
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+        }
+        try {
+            conn.createStatement().execute("ALTER VIEW v2 DROP COLUMN v2");
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), e.getErrorCode());
+        }
+        conn.createStatement().execute("ALTER VIEW v2 DROP COLUMN v3");
+        
+    }
+    
+    @Test
+    public void testReadOnlyViewWithCaseSensitiveTableNames() throws Exception {
+        Connection earlierCon = DriverManager.getConnection(getUrl());
+        Connection conn = DriverManager.getConnection(getUrl());
+        String ddl = "CREATE TABLE \"case_SENSITIVE_table\" (k INTEGER NOT NULL PRIMARY KEY, v1 DATE)";
+        conn.createStatement().execute(ddl);
+        ddl = "CREATE VIEW \"v\" (v2 VARCHAR) AS SELECT * FROM \"case_SENSITIVE_table\" WHERE k > 5";
+        conn.createStatement().execute(ddl);
+        try {
+            conn.createStatement().execute("UPSERT INTO \"v\" VALUES(1)");
+            fail();
+        } catch (ReadOnlyTableException e) {
+            
+        }
+        for (int i = 0; i < 10; i++) {
+            conn.createStatement().execute("UPSERT INTO \"case_SENSITIVE_table\" VALUES(" + i + ")");
+        }
+        conn.commit();
+        
+        int count = 0;
+        ResultSet rs = conn.createStatement().executeQuery("SELECT k FROM \"v\"");
+        while (rs.next()) {
+            count++;
+            assertEquals(count + 5, rs.getInt(1));
+        }
+        assertEquals(4, count);
+        count = 0;
+        rs = earlierCon.createStatement().executeQuery("SELECT k FROM \"v\"");
+        while (rs.next()) {
+            count++;
+            assertEquals(count + 5, rs.getInt(1));
+        }
+        assertEquals(4, count);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
new file mode 100644
index 0000000..6d1e995
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
@@ -0,0 +1,29 @@
+/*
+ * 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 org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
+import org.apache.phoenix.util.SchemaUtil;
+
+public class BaseIndexIT 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 DATA_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "T");
+    public static final String INDEX_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "I");
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java
new file mode 100644
index 0000000..4495877
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseMutableIndexIT.java
@@ -0,0 +1,134 @@
+/*
+ * 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.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.util.TestUtil;
+
+public class BaseMutableIndexIT extends BaseIndexIT {
+    public BaseMutableIndexIT() {
+    }
+
+    protected static void createTestTable() throws SQLException {
+        String ddl = "create table if not exists " + DATA_TABLE_FULL_NAME + "(" +
+                "   varchar_pk VARCHAR NOT NULL, " +
+                "   char_pk CHAR(5) NOT NULL, " +
+                "   int_pk INTEGER NOT NULL, "+ 
+                "   long_pk BIGINT NOT NULL, " +
+                "   decimal_pk DECIMAL(31, 10) NOT NULL, " +
+                "   a.varchar_col1 VARCHAR, " +
+                "   a.char_col1 CHAR(5), " +
+                "   a.int_col1 INTEGER, " +
+                "   a.long_col1 BIGINT, " +
+                "   a.decimal_col1 DECIMAL(31, 10), " +
+                "   b.varchar_col2 VARCHAR, " +
+                "   b.char_col2 CHAR(5), " +
+                "   b.int_col2 INTEGER, " +
+                "   b.long_col2 BIGINT, " +
+                "   b.decimal_col2 DECIMAL, " +
+                "   b.date_col DATE " + 
+                "   CONSTRAINT pk PRIMARY KEY (varchar_pk, char_pk, int_pk, long_pk DESC, decimal_pk))";
+            Properties props = new Properties(TEST_PROPERTIES);
+            Connection conn = DriverManager.getConnection(getUrl(), props);
+            conn.createStatement().execute(ddl);
+            conn.close();
+    }
+    
+    // Populate the test table with data.
+    protected static void populateTestTable() throws SQLException {
+        populateTestTable(null);
+    }
+    // Populate the test table with data.
+    protected static void populateTestTable(Date date) throws SQLException {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            String upsert = "UPSERT INTO " + DATA_TABLE_FULL_NAME
+                    + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+            PreparedStatement stmt = conn.prepareStatement(upsert);
+            stmt.setString(1, "varchar1");
+            stmt.setString(2, "char1");
+            stmt.setInt(3, 1);
+            stmt.setLong(4, 1L);
+            stmt.setBigDecimal(5, new BigDecimal("1.1"));
+            stmt.setString(6, "varchar_a");
+            stmt.setString(7, "chara");
+            stmt.setInt(8, 2);
+            stmt.setLong(9, 2L);
+            stmt.setBigDecimal(10, new BigDecimal("2.1"));
+            stmt.setString(11, "varchar_b");
+            stmt.setString(12, "charb");
+            stmt.setInt(13, 3);
+            stmt.setLong(14, 3L);
+            stmt.setBigDecimal(15, new BigDecimal("3.1"));
+            stmt.setDate(16, date == null ? null : new Date(date.getTime() + TestUtil.MILLIS_IN_DAY));
+            stmt.executeUpdate();
+            
+            stmt.setString(1, "varchar2");
+            stmt.setString(2, "char2");
+            stmt.setInt(3, 2);
+            stmt.setLong(4, 2L);
+            stmt.setBigDecimal(5, new BigDecimal("2.2"));
+            stmt.setString(6, "varchar_a");
+            stmt.setString(7, "chara");
+            stmt.setInt(8, 3);
+            stmt.setLong(9, 3L);
+            stmt.setBigDecimal(10, new BigDecimal("3.2"));
+            stmt.setString(11, "varchar_b");
+            stmt.setString(12, "charb");
+            stmt.setInt(13, 4);
+            stmt.setLong(14, 4L);
+            stmt.setBigDecimal(15, new BigDecimal("4.2"));
+            stmt.setDate(16, date);
+            stmt.executeUpdate();
+            
+            stmt.setString(1, "varchar3");
+            stmt.setString(2, "char3");
+            stmt.setInt(3, 3);
+            stmt.setLong(4, 3L);
+            stmt.setBigDecimal(5, new BigDecimal("3.3"));
+            stmt.setString(6, "varchar_a");
+            stmt.setString(7, "chara");
+            stmt.setInt(8, 4);
+            stmt.setLong(9, 4L);
+            stmt.setBigDecimal(10, new BigDecimal("4.3"));
+            stmt.setString(11, "varchar_b");
+            stmt.setString(12, "charb");
+            stmt.setInt(13, 5);
+            stmt.setLong(14, 5L);
+            stmt.setBigDecimal(15, new BigDecimal("5.3"));
+            stmt.setDate(16, date == null ? null : new Date(date.getTime() + 2 * TestUtil.MILLIS_IN_DAY));
+            stmt.executeUpdate();
+            
+            conn.commit();
+        } finally {
+            conn.close();
+        }
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/DropViewIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/DropViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/DropViewIT.java
new file mode 100644
index 0000000..499b69e
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/DropViewIT.java
@@ -0,0 +1,99 @@
+/*
+ * 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.apache.phoenix.util.TestUtil.HBASE_NATIVE;
+import static org.apache.phoenix.util.TestUtil.HBASE_NATIVE_SCHEMA_NAME;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.SchemaUtil;
+
+public class DropViewIT extends BaseMutableIndexIT {
+    private static final byte[] HBASE_NATIVE_BYTES = SchemaUtil.getTableNameAsBytes(HBASE_NATIVE_SCHEMA_NAME, HBASE_NATIVE);
+    private static final byte[] FAMILY_NAME = Bytes.toBytes(SchemaUtil.normalizeIdentifier("1"));
+    
+    @BeforeClass 
+    public static void doSetup() throws Exception {
+        Map<String,String> props = Maps.newHashMapWithExpectedSize(1);
+        // Drop the HBase table metadata for this test
+        props.put(QueryServices.DROP_METADATA_ATTRIB, Boolean.toString(true));
+        // Must update config before starting server
+        startServer(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
+    }
+    
+    @Test
+    public void testDropViewKeepsHTable() throws Exception {
+        HBaseAdmin admin = driver.getConnectionQueryServices(getUrl(), TEST_PROPERTIES).getAdmin();
+        try {
+            try {
+                admin.disableTable(HBASE_NATIVE_BYTES);
+                admin.deleteTable(HBASE_NATIVE_BYTES);
+            } catch (org.apache.hadoop.hbase.TableNotFoundException e) {
+            }
+            HTableDescriptor descriptor = new HTableDescriptor(HBASE_NATIVE_BYTES);
+            HColumnDescriptor columnDescriptor =  new HColumnDescriptor(FAMILY_NAME);
+            columnDescriptor.setKeepDeletedCells(true);
+            descriptor.addFamily(columnDescriptor);
+            admin.createTable(descriptor);
+        } finally {
+            admin.close();
+        }
+        
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.createStatement().execute("create view " + HBASE_NATIVE +
+                "   (uint_key unsigned_int not null," +
+                "    ulong_key unsigned_long not null," +
+                "    string_key varchar not null,\n" +
+                "    \"1\".uint_col unsigned_int," +
+                "    \"1\".ulong_col unsigned_long" +
+                "    CONSTRAINT pk PRIMARY KEY (uint_key, ulong_key, string_key))\n" +
+                     HColumnDescriptor.DATA_BLOCK_ENCODING + "='" + DataBlockEncoding.NONE + "'");
+        conn.createStatement().execute("drop view " + HBASE_NATIVE);
+        
+        admin = driver.getConnectionQueryServices(getUrl(), TEST_PROPERTIES).getAdmin();
+        try {
+            try {
+                admin.disableTable(HBASE_NATIVE_BYTES);
+                admin.deleteTable(HBASE_NATIVE_BYTES);
+            } catch (org.apache.hadoop.hbase.TableNotFoundException e) {
+                fail(); // The underlying HBase table should still exist
+            }
+        } finally {
+            admin.close();
+        }
+    }
+}
+        

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/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
new file mode 100644
index 0000000..cd3a8fb
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java
@@ -0,0 +1,367 @@
+/*
+ * 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.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA;
+import static org.apache.phoenix.util.TestUtil.INDEX_DATA_TABLE;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+
+
+public class ImmutableIndexIT extends BaseHBaseManagedTimeIT {
+    // Populate the test table with data.
+    private static void populateTestTable() throws SQLException {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        try {
+            String upsert = "UPSERT INTO " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+                    + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+            PreparedStatement stmt = conn.prepareStatement(upsert);
+            stmt.setString(1, "varchar1");
+            stmt.setString(2, "char1");
+            stmt.setInt(3, 1);
+            stmt.setLong(4, 1L);
+            stmt.setBigDecimal(5, new BigDecimal(1.0));
+            stmt.setString(6, "varchar_a");
+            stmt.setString(7, "chara");
+            stmt.setInt(8, 2);
+            stmt.setLong(9, 2L);
+            stmt.setBigDecimal(10, new BigDecimal(2.0));
+            stmt.setString(11, "varchar_b");
+            stmt.setString(12, "charb");
+            stmt.setInt(13, 3);
+            stmt.setLong(14, 3L);
+            stmt.setBigDecimal(15, new BigDecimal(3.0));
+            stmt.executeUpdate();
+            
+            stmt.setString(1, "varchar2");
+            stmt.setString(2, "char2");
+            stmt.setInt(3, 2);
+            stmt.setLong(4, 2L);
+            stmt.setBigDecimal(5, new BigDecimal(2.0));
+            stmt.setString(6, "varchar_a");
+            stmt.setString(7, "chara");
+            stmt.setInt(8, 3);
+            stmt.setLong(9, 3L);
+            stmt.setBigDecimal(10, new BigDecimal(3.0));
+            stmt.setString(11, "varchar_b");
+            stmt.setString(12, "charb");
+            stmt.setInt(13, 4);
+            stmt.setLong(14, 4L);
+            stmt.setBigDecimal(15, new BigDecimal(4.0));
+            stmt.executeUpdate();
+            
+            stmt.setString(1, "varchar3");
+            stmt.setString(2, "char3");
+            stmt.setInt(3, 3);
+            stmt.setLong(4, 3L);
+            stmt.setBigDecimal(5, new BigDecimal(3.0));
+            stmt.setString(6, "varchar_a");
+            stmt.setString(7, "chara");
+            stmt.setInt(8, 4);
+            stmt.setLong(9, 4L);
+            stmt.setBigDecimal(10, new BigDecimal(4.0));
+            stmt.setString(11, "varchar_b");
+            stmt.setString(12, "charb");
+            stmt.setInt(13, 5);
+            stmt.setLong(14, 5L);
+            stmt.setBigDecimal(15, new BigDecimal(5.0));
+            stmt.executeUpdate();
+            
+            conn.commit();
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testIndexWithNullableFixedWithCols() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        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)";
+        PreparedStatement stmt = conn.prepareStatement(ddl);
+        stmt.execute();
+        
+        String query = "SELECT char_col1, int_col1 from " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE;
+        ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query);
+        assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER INDEX_TEST.IDX", QueryUtil.getExplainPlan(rs));
+        
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals("chara", rs.getString(1));
+        assertEquals(2, rs.getInt(2));
+        assertTrue(rs.next());
+        assertEquals("chara", rs.getString(1));
+        assertEquals(3, rs.getInt(2));
+        assertTrue(rs.next());
+        assertEquals("chara", rs.getString(1));
+        assertEquals(4, rs.getInt(2));
+        assertFalse(rs.next());
+        
+        conn.createStatement().execute("DROP INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE);
+        
+        query = "SELECT char_col1, int_col1 from " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        
+        query = "SELECT char_col1, int_col1 from IDX ";
+        try{
+            rs = conn.createStatement().executeQuery(query);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.TABLE_UNDEFINED.getErrorCode(), e.getErrorCode());
+        }
+        
+        
+    }
+    
+    private void assertImmutableRows(Connection conn, String fullTableName, boolean expectedValue) throws SQLException {
+        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
+        assertEquals(expectedValue, pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).isImmutableRows());
+    }
+    
+    @Test
+    public void testAlterTableWithImmutability() throws Exception {
+
+        String query;
+        ResultSet rs;
+        String fullTableName = "T";
+
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+
+        conn.createStatement().execute(
+            "CREATE TABLE t (k VARCHAR NOT NULL PRIMARY KEY, v VARCHAR)  ");
+        
+        query = "SELECT * FROM t";
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
+
+        assertImmutableRows(conn,fullTableName, false);
+        conn.createStatement().execute("ALTER TABLE t SET IMMUTABLE_ROWS=true");
+        assertImmutableRows(conn,fullTableName, true);
+        
+        
+        conn.createStatement().execute("ALTER TABLE t SET immutable_rows=false");
+        assertImmutableRows(conn,fullTableName, false);
+    }
+    
+    @Test
+    public void testDeleteFromAllPKColumnIndex() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        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)";
+        PreparedStatement stmt = conn.prepareStatement(ddl);
+        stmt.execute();
+        
+        ResultSet rs;
+        
+        rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE);
+        assertTrue(rs.next());
+        assertEquals(3,rs.getInt(1));
+        rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + "IDX");
+        assertTrue(rs.next());
+        assertEquals(3,rs.getInt(1));
+        
+        String dml = "DELETE from " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE +
+                " WHERE long_col2 = 4";
+        assertEquals(1,conn.createStatement().executeUpdate(dml));
+        conn.commit();
+        
+        String query = "SELECT /*+ NO_INDEX */ long_pk FROM " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals(1L, rs.getLong(1));
+        assertTrue(rs.next());
+        assertEquals(3L, rs.getLong(1));
+        assertFalse(rs.next());
+        
+        query = "SELECT long_pk FROM " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals(1L, rs.getLong(1));
+        assertTrue(rs.next());
+        assertEquals(3L, rs.getLong(1));
+        assertFalse(rs.next());
+        
+        query = "SELECT * FROM " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + "IDX" ;
+        rs = conn.createStatement().executeQuery(query);
+        assertTrue(rs.next());
+        assertEquals(1L, rs.getLong(1));
+        assertTrue(rs.next());
+        assertEquals(3L, rs.getLong(1));
+        assertFalse(rs.next());
+        
+        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);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
+        populateTestTable();
+        String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+                + " (long_col1)";
+        PreparedStatement stmt = conn.prepareStatement(ddl);
+        stmt.execute();
+        
+        ResultSet rs;
+        
+        rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE);
+        assertTrue(rs.next());
+        assertEquals(3,rs.getInt(1));
+        rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + "IDX");
+        assertTrue(rs.next());
+        assertEquals(3,rs.getInt(1));
+        
+        conn.setAutoCommit(true);
+        String dml = "DELETE from " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE +
+                " WHERE long_col2 = 4";
+        try {
+            conn.createStatement().execute(dml);
+            fail();
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.NO_DELETE_IF_IMMUTABLE_INDEX.getErrorCode(), e.getErrorCode());
+        }
+            
+        conn.createStatement().execute("DROP TABLE " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE);
+    }
+    
+    @Test
+    public void testGroupByCount() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
+        populateTestTable();
+        String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+                + " (int_col2)";
+        PreparedStatement stmt = conn.prepareStatement(ddl);
+        stmt.execute();
+        
+        ResultSet rs;
+        
+        rs = conn.createStatement().executeQuery("SELECT int_col2, COUNT(*) FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE + " GROUP BY int_col2");
+        assertTrue(rs.next());
+        assertEquals(1,rs.getInt(2));
+    }
+    
+    @Test   
+    public void testSelectDistinctOnTableWithSecondaryImmutableIndex() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
+        populateTestTable();
+        String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+                + " (int_col2)";
+        Connection conn = null;
+        PreparedStatement stmt = null;
+
+        try {
+            try {
+                conn = DriverManager.getConnection(getUrl(), props);
+                conn.setAutoCommit(false);
+                stmt = conn.prepareStatement(ddl);
+                stmt.execute();
+                ResultSet rs = conn.createStatement().executeQuery("SELECT distinct int_col2 FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE + " where int_col2 > 0");
+                assertTrue(rs.next());
+                assertEquals(3, rs.getInt(1));
+                assertTrue(rs.next());
+                assertEquals(4, rs.getInt(1));
+                assertTrue(rs.next());
+                assertEquals(5, rs.getInt(1));
+                assertFalse(rs.next());
+            } finally {
+                if (stmt != null) {
+                    stmt.close();
+                }
+            } 
+        } finally {
+            if (conn != null) {
+                conn.close();
+            }
+        }
+    }
+    
+    @Test
+    public void testInClauseWithIndexOnColumnOfUsignedIntType() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = null;
+        PreparedStatement stmt = null;
+        ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
+        populateTestTable();
+        String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+                + " (int_col1)";
+        try {
+            try {
+                conn = DriverManager.getConnection(getUrl(), props);
+                conn.setAutoCommit(false);
+                stmt = conn.prepareStatement(ddl);
+                stmt.execute();
+                ResultSet rs = conn.createStatement().executeQuery("SELECT int_col1 FROM " +INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE + " where int_col1 IN (1, 2, 3, 4)");
+                assertTrue(rs.next());
+                assertEquals(2, rs.getInt(1));
+                assertTrue(rs.next());
+                assertEquals(3, rs.getInt(1));
+                assertTrue(rs.next());
+                assertEquals(4, rs.getInt(1));
+                assertFalse(rs.next());
+            } finally {
+                if(stmt != null) {
+                    stmt.close();
+                }
+            } 
+        } finally {
+            if(conn != null) {
+                conn.close();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
new file mode 100644
index 0000000..d81140b
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java
@@ -0,0 +1,380 @@
+/*
+ * 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.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA;
+import static org.apache.phoenix.util.TestUtil.INDEX_DATA_TABLE;
+import static org.apache.phoenix.util.TestUtil.MUTABLE_INDEX_DATA_TABLE;
+import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT;
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.AmbiguousColumnException;
+import org.apache.phoenix.schema.PIndexState;
+import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.StringUtil;
+import org.apache.phoenix.util.TestUtil;
+import org.junit.Test;
+
+
+public class IndexMetadataIT extends BaseHBaseManagedTimeIT {
+
+	private enum Order {ASC, DESC};
+	
+    private static void assertIndexInfoMetadata(ResultSet rs, String schemaName, String dataTableName, String indexName, int colPos, String colName, Order order) throws SQLException {
+        assertTrue(rs.next());
+        assertEquals(null,rs.getString(1));
+        assertEquals(schemaName, rs.getString(2));
+        assertEquals(dataTableName, rs.getString(3));
+        assertEquals(Boolean.TRUE, rs.getBoolean(4));
+        assertEquals(null,rs.getString(5));
+        assertEquals(indexName, rs.getString(6));
+        assertEquals(DatabaseMetaData.tableIndexOther, rs.getShort(7));
+        assertEquals(colPos, rs.getShort(8));
+        assertEquals(colName, rs.getString(9));
+        assertEquals(order == Order.ASC ? "A" : order == Order.DESC ? "D" : null, rs.getString(10));
+        assertEquals(0,rs.getInt(11));
+        assertTrue(rs.wasNull());
+        assertEquals(0,rs.getInt(12));
+        assertTrue(rs.wasNull());
+        assertEquals(null,rs.getString(13));
+    }
+
+    private static void assertIndexInfoMetadata(ResultSet rs, String schemaName, String dataTableName, String indexName, int colPos, String colName, Order order, int type) throws SQLException {
+        assertTrue(rs.next());
+        assertEquals(null,rs.getString(1));
+        assertEquals(schemaName, rs.getString(2));
+        assertEquals(dataTableName, rs.getString(3));
+        assertEquals(Boolean.TRUE, rs.getBoolean(4));
+        assertEquals(null,rs.getString(5));
+        assertEquals(indexName, rs.getString(6));
+        assertEquals(DatabaseMetaData.tableIndexOther, rs.getShort(7));
+        assertEquals(colPos, rs.getShort(8));
+        assertEquals(colName, rs.getString(9));
+        assertEquals(order == Order.ASC ? "A" : order == Order.DESC ? "D" : null, rs.getString(10));
+        assertEquals(0,rs.getInt(11));
+        assertTrue(rs.wasNull());
+        assertEquals(0,rs.getInt(12));
+        assertTrue(rs.wasNull());
+        assertEquals(null,rs.getString(13));
+        assertEquals(type,rs.getInt(14));
+    }
+	
+    private static void assertActiveIndex(Connection conn, String schemaName, String tableName) throws SQLException {
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
+        conn.createStatement().executeQuery("SELECT count(*) FROM " + fullTableName).next(); // client side cache will update
+        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
+        pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).getIndexMaintainers(ptr);
+        assertTrue(ptr.getLength() > 0);
+    }
+    
+    private static void assertNoActiveIndex(Connection conn, String schemaName, String tableName) throws SQLException {
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
+        conn.createStatement().executeQuery("SELECT count(*) FROM " + fullTableName).next(); // client side cache will update
+        PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class);
+        pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).getIndexMaintainers(ptr);
+        assertTrue(ptr.getLength() == 0);
+    }
+    
+    @Test
+    public void testIndexCreation() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            ensureTableCreated(getUrl(), MUTABLE_INDEX_DATA_TABLE);
+            String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE
+                    + " (varchar_col1 ASC, varchar_col2 ASC, int_pk DESC)"
+                    + " INCLUDE (int_col1, int_col2)";
+            PreparedStatement stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            
+            // Verify the metadata for index is correct.
+            ResultSet rs = conn.getMetaData().getIndexInfo(null, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, false, false);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 1, "A:VARCHAR_COL1", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 2, "B:VARCHAR_COL2", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 3, ":INT_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 4, ":VARCHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 5, ":CHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 6, ":LONG_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 7, ":DECIMAL_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 8, "A:INT_COL1", null);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 9, "B:INT_COL2", null);
+            assertFalse(rs.next());
+            
+            rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), StringUtil.escapeLike("IDX"), new String[] {PTableType.INDEX.getValue().getString() });
+            assertTrue(rs.next());
+            assertEquals(PIndexState.ACTIVE.toString(), rs.getString("INDEX_STATE"));
+
+            // Verify that there is a row inserted into the data table for the index table.
+            rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX");
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(1));
+            assertFalse(rs.next());
+            
+            assertActiveIndex(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE);
+            
+            ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + " UNUSABLE";
+            conn.createStatement().execute(ddl);
+            // Verify the metadata for index is correct.
+            rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), "IDX", new String[] {PTableType.INDEX.toString()});
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(3));
+            assertEquals(PIndexState.INACTIVE.toString(), rs.getString("INDEX_STATE"));
+            assertFalse(rs.next());
+            
+            assertActiveIndex(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE);
+
+            ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + " USABLE";
+            conn.createStatement().execute(ddl);
+            // Verify the metadata for index is correct.
+            rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), "IDX", new String[] {PTableType.INDEX.toString()});
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(3));
+            assertEquals(PIndexState.ACTIVE.toString(), rs.getString("INDEX_STATE"));
+            assertFalse(rs.next());
+            
+            assertActiveIndex(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE);
+
+            ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + " DISABLE";
+            conn.createStatement().execute(ddl);
+            // Verify the metadata for index is correct.
+            rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), "IDX", new String[] {PTableType.INDEX.toString()});
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(3));
+            assertEquals(PIndexState.DISABLE.toString(), rs.getString("INDEX_STATE"));
+            assertFalse(rs.next());
+            
+            assertNoActiveIndex(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE);
+
+            try {
+                ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + " USABLE";
+                conn.createStatement().execute(ddl);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.INVALID_INDEX_STATE_TRANSITION.getErrorCode(), e.getErrorCode());
+            }
+            try {
+                ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + " UNUSABLE";
+                conn.createStatement().execute(ddl);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.INVALID_INDEX_STATE_TRANSITION.getErrorCode(), e.getErrorCode());
+            }
+            
+            ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE + " REBUILD";
+            conn.createStatement().execute(ddl);
+            // Verify the metadata for index is correct.
+            rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), "IDX", new String[] {PTableType.INDEX.toString()});
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(3));
+            assertEquals(PIndexState.ACTIVE.toString(), rs.getString("INDEX_STATE"));
+            assertFalse(rs.next());
+            
+            assertActiveIndex(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE);
+
+            ddl = "DROP INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+            stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            
+            assertNoActiveIndex(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE);
+
+           // Assert the rows for index table is completely removed.
+            rs = conn.getMetaData().getIndexInfo(null, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, false, false);
+            assertFalse(rs.next());
+            
+            // Assert the row in the original data table is removed.
+            // Verify that there is a row inserted into the data table for the index table.
+            rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX");
+            assertFalse(rs.next());
+            
+            // Create another two indexes, and drops the table, verifies the indexes are dropped as well.
+            ddl = "CREATE INDEX IDX1 ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE
+                    + " (varchar_col1 ASC, varchar_col2 ASC, int_pk DESC)"
+                    + " INCLUDE (int_col1, int_col2)";
+            stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            
+            ddl = "CREATE INDEX IDX2 ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE
+                    + " (varchar_col1 ASC, varchar_col2 ASC, int_pk DESC)"
+                    + " INCLUDE (long_pk, int_col2)";
+            stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            rs = conn.getMetaData().getIndexInfo(null, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, false, false);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 1, "A:VARCHAR_COL1", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 2, "B:VARCHAR_COL2", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 3, ":INT_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 4, ":VARCHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 5, ":CHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 6, ":LONG_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 7, ":DECIMAL_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 8, "A:INT_COL1", null);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 9, "B:INT_COL2", null);
+
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 1, "A:VARCHAR_COL1", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 2, "B:VARCHAR_COL2", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 3, ":INT_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 4, ":VARCHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 5, ":CHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 6, ":LONG_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 7, ":DECIMAL_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 8, "B:INT_COL2", null);
+            assertFalse(rs.next());
+            
+            ddl = "DROP TABLE " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + MUTABLE_INDEX_DATA_TABLE;
+            stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            
+            rs = conn.getMetaData().getIndexInfo(null, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, false, false);
+            assertFalse(rs.next());
+            rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1");
+            assertFalse(rs.next());
+            rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2");
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testIndexDefinitionWithNullableFixedWidthColInPK() throws Exception {
+    	// If we have nullable fixed width column in the PK, we convert those types into a compatible variable type
+    	// column. The definition is defined in IndexUtil.getIndexColumnDataType.
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            ensureTableCreated(getUrl(), INDEX_DATA_TABLE);
+            String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+                    + " (char_col1 ASC, int_col2 ASC, long_col2 DESC)"
+                    + " INCLUDE (int_col1)";
+            PreparedStatement stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            
+            // Verify the CHAR, INT and LONG are converted to right type.
+            ResultSet rs = conn.getMetaData().getIndexInfo(null, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, false, false);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 1, "A:CHAR_COL1", Order.ASC, Types.VARCHAR);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 2, "B:INT_COL2", Order.ASC, Types.DECIMAL);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 3, "B:LONG_COL2", Order.DESC, Types.DECIMAL);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 4, ":VARCHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 5, ":CHAR_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 6, ":INT_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 7, ":LONG_PK", Order.DESC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 8, ":DECIMAL_PK", Order.ASC);
+            assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 9, "A:INT_COL1", null);
+            assertFalse(rs.next());
+            
+            rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX");
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(1));
+            assertFalse(rs.next());
+            
+            ddl = "ALTER INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE + " UNUSABLE";
+            conn.createStatement().execute(ddl);
+            // Verify the metadata for index is correct.
+            rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), "IDX", new String[] {PTableType.INDEX.toString()});
+            assertTrue(rs.next());
+            assertEquals("IDX", rs.getString(3));
+            assertEquals(PIndexState.INACTIVE.toString(), rs.getString("INDEX_STATE"));
+            assertFalse(rs.next());
+            
+            ddl = "DROP INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE;
+            stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            
+            // Assert the rows for index table is completely removed.
+            rs = conn.getMetaData().getIndexInfo(null, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, false, false);
+            assertFalse(rs.next());
+            
+            // Assert the row in the original data table is removed.
+            // Verify that there is a row inserted into the data table for the index table.
+            rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX");
+            assertFalse(rs.next());
+        } finally {
+            conn.close();
+        }
+    }
+
+    @Test
+    public void testIndexDefinitionWithRepeatedColumns() throws Exception {
+    	// Test index creation when the columns is included in both the PRIMARY and INCLUDE section. Test de-duplication.
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        try {
+            ensureTableCreated(getUrl(), TestUtil.INDEX_DATA_TABLE);
+            String ddl = "CREATE INDEX IDX ON " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE
+            		+ " (a.int_col1, a.long_col1, b.int_col2, b.long_col2)"
+            		+ " INCLUDE(int_col1, int_col2)";
+            PreparedStatement stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            fail("Should have caught exception.");
+        } catch (SQLException e) {
+            assertEquals(SQLExceptionCode.COLUMN_EXIST_IN_DEF.getErrorCode(), e.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testIndexDefinitionWithSameColumnNamesInTwoFamily() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        String ddl = "create table test_table (char_pk varchar not null,"
+        		+ " a.int_col integer, a.long_col integer,"
+        		+ " b.int_col integer, b.long_col integer"
+        		+ " constraint pk primary key (char_pk))";
+        PreparedStatement stmt = conn.prepareStatement(ddl);
+        stmt.execute();
+        
+        ddl = "CREATE INDEX IDX1 ON test_table (a.int_col, b.int_col)";
+        stmt = conn.prepareStatement(ddl);
+        stmt.execute();
+        try {
+            ddl = "CREATE INDEX IDX2 ON test_table (int_col)";
+            stmt = conn.prepareStatement(ddl);
+            stmt.execute();
+            fail("Should have caught exception");
+        } catch (AmbiguousColumnException e) {
+            assertEquals(SQLExceptionCode.AMBIGUOUS_COLUMN.getErrorCode(), e.getErrorCode());
+        } finally {
+            conn.close();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexTestUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexTestUtil.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexTestUtil.java
new file mode 100644
index 0000000..42cfd12
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexTestUtil.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end.index;
+
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_FAMILY;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_NAME;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_SCHEM;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TENANT_ID;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.BitSet;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.hbase.KeyValue;
+import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.Mutation;
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import com.google.common.collect.Lists;
+
+import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
+import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.ColumnNotFoundException;
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PColumnFamily;
+import org.apache.phoenix.schema.PDataType;
+import org.apache.phoenix.schema.PRow;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.RowKeySchema;
+import org.apache.phoenix.util.IndexUtil;
+import org.apache.phoenix.util.MetaDataUtil;
+import org.apache.phoenix.util.SchemaUtil;
+
+public class IndexTestUtil {
+
+    // the normal db metadata interface is insufficient for all fields needed for an
+    // index table test.
+    private static final String SELECT_DATA_INDEX_ROW = "SELECT " + COLUMN_FAMILY
+            + " FROM "
+            + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE
+            + "\" WHERE "
+            + TENANT_ID + " IS NULL AND " + TABLE_SCHEM + "=? AND " + TABLE_NAME + "=? AND " + COLUMN_NAME + " IS NULL AND " + COLUMN_FAMILY + "=?";
+    
+    public static ResultSet readDataTableIndexRow(Connection conn, String schemaName, String tableName, String indexName) throws SQLException {
+        PreparedStatement stmt = conn.prepareStatement(SELECT_DATA_INDEX_ROW);
+        stmt.setString(1, schemaName);
+        stmt.setString(2, tableName);
+        stmt.setString(3, indexName);
+        return stmt.executeQuery();
+    }
+    
+    
+    private static void coerceDataValueToIndexValue(PColumn dataColumn, PColumn indexColumn, ImmutableBytesWritable ptr) {
+        PDataType dataType = dataColumn.getDataType();
+        // TODO: push to RowKeySchema? 
+        SortOrder dataModifier = dataColumn.getSortOrder();
+        PDataType indexType = indexColumn.getDataType();
+        SortOrder indexModifier = indexColumn.getSortOrder();
+        // We know ordinal position will match pk position, because you cannot
+        // alter an index table.
+        indexType.coerceBytes(ptr, dataType, dataModifier, indexModifier);
+    }
+    
+    public static List<Mutation> generateIndexData(PTable index, PTable table,
+            List<Mutation> dataMutations, ImmutableBytesWritable ptr, KeyValueBuilder builder)
+            throws SQLException {
+        List<Mutation> indexMutations = Lists.newArrayListWithExpectedSize(dataMutations.size());
+        for (Mutation dataMutation : dataMutations) {
+            indexMutations.addAll(generateIndexData(index, table, dataMutation, ptr, builder));
+        }
+        return indexMutations;
+    }
+
+    public static List<Mutation> generateIndexData(PTable indexTable, PTable dataTable,
+            Mutation dataMutation, ImmutableBytesWritable ptr, KeyValueBuilder builder)
+            throws SQLException {
+        byte[] dataRowKey = dataMutation.getRow();
+        RowKeySchema dataRowKeySchema = dataTable.getRowKeySchema();
+        List<PColumn> dataPKColumns = dataTable.getPKColumns();
+        int i = 0;
+        int indexOffset = 0;
+        Boolean hasValue;
+        // Skip salt column
+        int maxOffset = dataRowKey.length;
+        dataRowKeySchema.iterator(dataRowKey, ptr, dataTable.getBucketNum() == null ? i : ++i);
+        List<PColumn> indexPKColumns = indexTable.getPKColumns();
+        List<PColumn> indexColumns = indexTable.getColumns();
+        int nIndexColumns = indexPKColumns.size();
+        int maxIndexValues = indexColumns.size() - nIndexColumns - indexOffset;
+        BitSet indexValuesSet = new BitSet(maxIndexValues);
+        byte[][] indexValues = new byte[indexColumns.size() - indexOffset][];
+        while ((hasValue = dataRowKeySchema.next(ptr, i, maxOffset)) != null) {
+            if (hasValue) {
+                PColumn dataColumn = dataPKColumns.get(i);
+                PColumn indexColumn = indexTable.getColumn(IndexUtil.getIndexColumnName(dataColumn));
+                coerceDataValueToIndexValue(dataColumn, indexColumn, ptr);
+                indexValues[indexColumn.getPosition()-indexOffset] = ptr.copyBytes();
+            }
+            i++;
+        }
+        PRow row;
+        long ts = MetaDataUtil.getClientTimeStamp(dataMutation);
+        if (dataMutation instanceof Delete && dataMutation.getFamilyMap().values().isEmpty()) {
+            indexTable.newKey(ptr, indexValues);
+            row = indexTable.newRow(builder, ts, ptr);
+            row.delete();
+        } else {
+            // If no column families in table, then nothing to look for 
+            if (!dataTable.getColumnFamilies().isEmpty()) {
+                for (Map.Entry<byte[],List<KeyValue>> entry : dataMutation.getFamilyMap().entrySet()) {
+                    PColumnFamily family = dataTable.getColumnFamily(entry.getKey());
+                    for (KeyValue kv : entry.getValue()) {
+                        byte[] cq = kv.getQualifier();
+                        if (Bytes.compareTo(QueryConstants.EMPTY_COLUMN_BYTES, cq) != 0) {
+                            try {
+                                PColumn dataColumn = family.getColumn(cq);
+                                PColumn indexColumn = indexTable.getColumn(IndexUtil.getIndexColumnName(family.getName().getString(), dataColumn.getName().getString()));
+                                ptr.set(kv.getBuffer(),kv.getValueOffset(),kv.getValueLength());
+                                coerceDataValueToIndexValue(dataColumn, indexColumn, ptr);
+                                indexValues[indexPKColumns.indexOf(indexColumn)-indexOffset] = ptr.copyBytes();
+                                if (!SchemaUtil.isPKColumn(indexColumn)) {
+                                    indexValuesSet.set(indexColumn.getPosition()-nIndexColumns-indexOffset);
+                                }
+                            } catch (ColumnNotFoundException e) {
+                                // Ignore as this means that the data column isn't in the index
+                            }
+                        }
+                    }
+                }
+            }
+            indexTable.newKey(ptr, indexValues);
+            row = indexTable.newRow(builder, ts, ptr);
+            int pos = 0;
+            while ((pos = indexValuesSet.nextSetBit(pos)) >= 0) {
+                int index = nIndexColumns + indexOffset + pos++;
+                PColumn indexColumn = indexColumns.get(index);
+                row.setValue(indexColumn, indexValues[index]);
+            }
+        }
+        return row.toRowMutations();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/8d6e2a58/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java
new file mode 100644
index 0000000..7bb9776
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java
@@ -0,0 +1,124 @@
+/*
+ * 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.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.TableNotFoundException;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.common.collect.Maps;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.query.ConnectionQueryServices;
+import org.apache.phoenix.schema.PIndexState;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.StringUtil;
+
+/**
+ * 
+ * Test for failure of region server to write to index table. Keep in its own
+ * separate test, as otherwise it makes tests run after it fail.
+ *
+ * 
+ * @since 2.1
+ */
+public class MutableIndexFailureIT extends BaseMutableIndexIT {
+    @BeforeClass 
+    public static void doSetup() throws Exception {
+        Map<String,String> props = Maps.newHashMapWithExpectedSize(1);
+        props.put("hbase.client.retries.number", Integer.toString(2));
+        props.put("hbase.client.pause", Integer.toString(5000));
+        // Must update config before starting server
+        startServer(getUrl(), new ReadOnlyProps(props.entrySet().iterator()));
+    }
+    
+    
+    private static void destroyIndexTable() throws Exception {
+        Properties props = new Properties(TEST_PROPERTIES);
+        ConnectionQueryServices services = DriverManager.getConnection(getUrl(), props).unwrap(PhoenixConnection.class).getQueryServices();
+        HBaseAdmin admin = services.getAdmin();
+        try {
+            admin.disableTable(INDEX_TABLE_FULL_NAME);
+            admin.deleteTable(INDEX_TABLE_FULL_NAME);
+        } catch (TableNotFoundException e) {
+        } finally {
+            admin.close();
+        }
+    }
+    
+    @Test
+    public void testWriteFailureDisablesIndex() throws Exception {
+        String query;
+        ResultSet rs;
+        
+        Properties props = new Properties(TEST_PROPERTIES);
+        Connection conn = DriverManager.getConnection(getUrl(), props);
+        conn.setAutoCommit(false);
+        conn.createStatement().execute("CREATE TABLE " + DATA_TABLE_FULL_NAME + " (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR)");
+        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) INCLUDE (v2)");
+        query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME;
+        rs = conn.createStatement().executeQuery(query);
+        assertFalse(rs.next());
+
+        // Verify the metadata for index is correct.
+        rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(SCHEMA_NAME), INDEX_TABLE_NAME, new String[] {PTableType.INDEX.toString()});
+        assertTrue(rs.next());
+        assertEquals(INDEX_TABLE_NAME, rs.getString(3));
+        assertEquals(PIndexState.ACTIVE.toString(), rs.getString("INDEX_STATE"));
+        assertFalse(rs.next());
+
+        destroyIndexTable();
+        
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?,?)");
+        stmt.setString(1,"a");
+        stmt.setString(2, "x");
+        stmt.setString(3, "1");
+        stmt.execute();
+        try {
+            conn.commit();
+            fail();
+        } catch (SQLException e) {
+        }
+        
+        // Verify the metadata for index is correct.
+        rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(SCHEMA_NAME), INDEX_TABLE_NAME, new String[] {PTableType.INDEX.toString()});
+        assertTrue(rs.next());
+        assertEquals(INDEX_TABLE_NAME, rs.getString(3));
+        assertEquals(PIndexState.DISABLE.toString(), rs.getString("INDEX_STATE"));
+        assertFalse(rs.next());
+    }
+}
\ No newline at end of file