phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sama...@apache.org
Subject phoenix git commit: Refactored set properties code to ConnectionQueryServices. Reduced rpc calls for setting properties and creating column families to 1
Date Thu, 15 Jan 2015 04:08:37 GMT
Repository: phoenix
Updated Branches:
  refs/heads/4.0 f38ede8bd -> 58c80a185


Refactored set properties code to ConnectionQueryServices. Reduced rpc calls for setting properties and creating column families to 1


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

Branch: refs/heads/4.0
Commit: 58c80a1857659962d695bc8e5fe22b4e401b7f0a
Parents: f38ede8
Author: Samarth <samarth.jain@salesforce.com>
Authored: Wed Jan 14 20:07:30 2015 -0800
Committer: Samarth <samarth.jain@salesforce.com>
Committed: Wed Jan 14 20:07:30 2015 -0800

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 145 +++++-
 .../phoenix/coprocessor/MetaDataProtocol.java   |   3 +-
 .../phoenix/query/ConnectionQueryServices.java  |   6 +-
 .../query/ConnectionQueryServicesImpl.java      | 437 ++++++++++++-------
 .../query/ConnectionlessQueryServicesImpl.java  |  11 +-
 .../query/DelegateConnectionQueryServices.java  |  13 +-
 .../apache/phoenix/schema/MetaDataClient.java   | 266 ++---------
 .../phoenix/compile/QueryCompilerTest.java      |   2 +-
 8 files changed, 466 insertions(+), 417 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index dcb6237..ac578c9 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -40,6 +40,7 @@ import java.util.Properties;
 
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.KeepDeletedCells;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.coprocessor.MetaDataProtocol;
@@ -55,6 +56,7 @@ import org.apache.phoenix.util.PropertiesUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -928,7 +930,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     @Test
     public void testAddColumnForNewColumnFamily() throws Exception {
         Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
-        String ddl = "CREATE TABLE T (\n"
+        String ddl = "CREATE TABLE testAddColumnForNewColumnFamily (\n"
                 +"ID1 VARCHAR(15) NOT NULL,\n"
                 +"ID2 VARCHAR(15) NOT NULL,\n"
                 +"CREATED_DATE DATE,\n"
@@ -937,8 +939,16 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
                 +"CONSTRAINT PK PRIMARY KEY (ID1, ID2)) SALT_BUCKETS = 8";
         Connection conn1 = DriverManager.getConnection(getUrl(), props);
         conn1.createStatement().execute(ddl);
-        ddl = "ALTER TABLE T ADD CF.STRING VARCHAR";
+        ddl = "ALTER TABLE testAddColumnForNewColumnFamily ADD CF.STRING VARCHAR";
         conn1.createStatement().execute(ddl);
+        try (HBaseAdmin admin = conn1.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+            HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes("testAddColumnForNewColumnFamily".toUpperCase())).getColumnFamilies();
+            assertEquals(2, columnFamilies.length);
+            assertEquals("0", columnFamilies[0].getNameAsString());
+            assertEquals(HColumnDescriptor.DEFAULT_TTL, columnFamilies[0].getTimeToLive());
+            assertEquals("CF", columnFamilies[1].getNameAsString());
+            assertEquals(HColumnDescriptor.DEFAULT_TTL, columnFamilies[1].getTimeToLive());
+        }
     }
 
     @Test
@@ -1024,7 +1034,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         assertImmutableRows(conn, "T3", true);
         ddl = "ALTER TABLE T3 SET COMPACTION_ENABLED = FALSE, VERSIONS = 10";
         conn.createStatement().execute(ddl);
-        ddl = "ALTER TABLE T3 SET COMPACTION_ENABLED = FALSE, CF1.MIN_VERSIONS = 1, CF2.MIN_VERSIONS = 3, MIN_VERSIONS = 8, IMMUTABLE_ROWS=false";
+        ddl = "ALTER TABLE T3 SET COMPACTION_ENABLED = FALSE, CF1.MIN_VERSIONS = 1, CF2.MIN_VERSIONS = 3, MIN_VERSIONS = 8, IMMUTABLE_ROWS=false, CF1.KEEP_DELETED_CELLS = true, KEEP_DELETED_CELLS = false";
         conn.createStatement().execute(ddl);
         assertImmutableRows(conn, "T3", false);
 
@@ -1036,14 +1046,17 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
             assertEquals("0", columnFamilies[0].getNameAsString());
             assertEquals(8, columnFamilies[0].getMinVersions());
             assertEquals(10, columnFamilies[0].getMaxVersions());
+            assertEquals(KeepDeletedCells.FALSE, columnFamilies[0].getKeepDeletedCells());
 
             assertEquals("CF1", columnFamilies[1].getNameAsString());
             assertEquals(1, columnFamilies[1].getMinVersions());
             assertEquals(10, columnFamilies[1].getMaxVersions());
+            assertEquals(KeepDeletedCells.TRUE, columnFamilies[1].getKeepDeletedCells());
 
             assertEquals("CF2", columnFamilies[2].getNameAsString());
             assertEquals(3, columnFamilies[2].getMinVersions());
             assertEquals(10, columnFamilies[2].getMaxVersions());
+            assertEquals(KeepDeletedCells.FALSE, columnFamilies[2].getKeepDeletedCells());
 
             assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED));
         }
@@ -1064,6 +1077,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ddl = "ALTER TABLE T4 SET CF.COMPACTION_ENABLED = FALSE";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode());
         }
@@ -1084,6 +1098,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ddl = "ALTER TABLE T5 SET CF.DISABLE_WAL = TRUE";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode());
         }
@@ -1125,6 +1140,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ddl = "ALTER TABLE T7 SET CF.REPLICATION_SCOPE = 1";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_FOUND.getErrorCode(), e.getErrorCode());
         }
@@ -1145,6 +1161,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ddl = "ALTER TABLE T8 SET DEFAULT_COLUMN_FAMILY = 'A'";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.DEFAULT_COLUMN_FAMILY_ONLY_ON_CREATE_TABLE.getErrorCode(), e.getErrorCode());
         }
@@ -1167,12 +1184,14 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ddl = "ALTER VIEW v SET REPLICATION_SCOPE = 1";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
         }
         ddl = "ALTER VIEW v SET COMPACTION_ENABLED = FALSE";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
         }
@@ -1201,6 +1220,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         ddl = "ALTER VIEW v SET DISABLE_WAL = TRUE";
         try {
             conn1.createStatement().execute(ddl);
+            fail();
         } catch (SQLException e) {
             assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode());
         }
@@ -1369,14 +1389,14 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     public void testSetPropertyAndAddColumnForDifferentColumnFamilies() throws Exception {
     	Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
     	Connection conn = DriverManager.getConnection(getUrl(), props);
-    	String ddl = "CREATE TABLE SETPROPDIFFCFSTABLE " +
-    			"  (a_string varchar not null, col1 integer, CF1.col2 integer, CF2.col3 integer" +
-    			"  CONSTRAINT pk PRIMARY KEY (a_string)) DEFAULT_COLUMN_FAMILY = 'XYZ'\n";
-    	try {
-    		conn.createStatement().execute(ddl);
-    		conn.createStatement().execute("ALTER TABLE SETPROPDIFFCFSTABLE ADD col4 integer, CF1.col5 integer, CF2.col6 integer, CF3.col7 integer CF1.REPLICATION_SCOPE=1, CF1.IN_MEMORY=false, IN_MEMORY=true ");
-    		try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
-                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes("SETPROPDIFFCFSTABLE")).getColumnFamilies();
+    	String ddl = "CREATE TABLE XYZ.SETPROPDIFFCFSTABLE " +
+                "  (a_string varchar not null, col1 integer, CF1.col2 integer, CF2.col3 integer" +
+                "  CONSTRAINT pk PRIMARY KEY (a_string)) DEFAULT_COLUMN_FAMILY = 'XYZ'\n";
+        try {
+            conn.createStatement().execute(ddl);
+            conn.createStatement().execute("ALTER TABLE XYZ.SETPROPDIFFCFSTABLE ADD col4 integer, CF1.col5 integer, CF2.col6 integer, CF3.col7 integer CF1.REPLICATION_SCOPE=1, CF1.IN_MEMORY=false, IN_MEMORY=true ");
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HColumnDescriptor[] columnFamilies = admin.getTableDescriptor(Bytes.toBytes(SchemaUtil.getTableName("XYZ", "SETPROPDIFFCFSTABLE"))).getColumnFamilies();
                 assertEquals(4, columnFamilies.length);
                 assertEquals("CF1", columnFamilies[0].getNameAsString());
                 assertFalse(columnFamilies[0].isInMemory());
@@ -1553,7 +1573,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     		conn.createStatement().execute(ddl);
     		fail();
     	} catch (SQLException e) {
-    		assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+    		assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL.getErrorCode(), e.getErrorCode());
     	} finally {
     		conn.close();
     	}
@@ -1782,6 +1802,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
     		ddl = "ALTER TABLE RANDMONPROPTABLE ADD NEWCF.COL3 INTEGER NEWCF.UNKNOWN_PROP='ABC'";
     		try {
     			conn.createStatement().execute(ddl);
+    			fail();
     		} catch (SQLException e) {
     			assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
     		}
@@ -1822,4 +1843,104 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT {
         stmt.close();
 
     }
+    
+    @Test
+    @Ignore //FIXME: https://issues.apache.org/jira/browse/PHOENIX-1581
+    public void testTogglingDisableWal() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            String ddl = "create table IF NOT EXISTS testReenablingWal ("
+                    + " id char(1) NOT NULL,"
+                    + " col1 integer NOT NULL,"
+                    + " col2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1, col2)"
+                    + " )";
+            conn.createStatement().execute(ddl);
+            asssertIsWALDisabled(conn, "testReenablingWal".toUpperCase(), false);
+            
+            ddl = "ALTER TABLE testReenablingWal SET DISABLE_WAL = true";
+            conn.createStatement().execute(ddl);
+            asssertIsWALDisabled(conn, "testReenablingWal".toUpperCase(), true);
+            
+            ddl = "ALTER TABLE testReenablingWal SET DISABLE_WAL = false";
+            conn.createStatement().execute(ddl);
+            asssertIsWALDisabled(conn, "testReenablingWal".toUpperCase(), false);
+            
+        } finally {
+            conn.close();
+        }
+    }
+    
+    @Test
+    public void testAddingPkColAndSettingProperties() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            String ddl = "create table IF NOT EXISTS testAddingPkColAndSettingProperties ("
+                    + " k1 char(1) NOT NULL,"
+                    + " k2 integer NOT NULL,"
+                    + " col1 bigint,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (k1, k2)"
+                    + " )";
+            conn.createStatement().execute(ddl);
+
+            // set HTableProperty when adding a pk column should fail
+            ddl = "ALTER TABLE testAddingPkColAndSettingProperties ADD k3 DECIMAL PRIMARY KEY COMPACTION_ENABLED = false";
+            try {
+                conn.createStatement().execute(ddl);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+            }
+
+            // set HColumnProperty when adding only a pk column should fail
+            ddl = "ALTER TABLE testAddingPkColAndSettingProperties ADD k3 DECIMAL PRIMARY KEY REPLICATION_SCOPE = 0";
+            try {
+                conn.createStatement().execute(ddl);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.SET_UNSUPPORTED_PROP_ON_ALTER_TABLE.getErrorCode(), e.getErrorCode());
+            }
+
+            // set phoenix table property when adding a pk column should fail
+            ddl = "ALTER TABLE testAddingPkColAndSettingProperties ADD k3 DECIMAL PRIMARY KEY DISABLE_WAL = true";
+            try {
+                conn.createStatement().execute(ddl);
+                fail();
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+            }
+
+            // set HColumnProperty property when adding a pk column and other key value columns should work
+            ddl = "ALTER TABLE testAddingPkColAndSettingProperties ADD k3 DECIMAL PRIMARY KEY, col2 bigint, CF.col3 bigint IN_MEMORY = true, CF.IN_MEMORY=false, CF.REPLICATION_SCOPE = 1";
+            conn.createStatement().execute(ddl);
+            String tableName = "testAddingPkColAndSettingProperties".toUpperCase();
+            // assert that k3 was added as new pk
+            ResultSet rs = conn.getMetaData().getPrimaryKeys("", "", tableName);
+            assertTrue(rs.next());
+            assertEquals("K1",rs.getString("COLUMN_NAME"));
+            assertEquals(1, rs.getShort("KEY_SEQ"));
+            assertTrue(rs.next());
+            assertEquals("K2",rs.getString("COLUMN_NAME"));
+            assertEquals(2, rs.getShort("KEY_SEQ"));
+            assertTrue(rs.next());
+            assertEquals("K3",rs.getString("COLUMN_NAME"));
+            assertEquals(3, rs.getShort("KEY_SEQ"));
+            assertFalse(rs.next());
+
+            try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) {
+                HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes(tableName));
+                HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies();
+                assertEquals(2, columnFamilies.length);
+                assertEquals("0", columnFamilies[0].getNameAsString());
+                assertEquals(true, columnFamilies[0].isInMemory());
+                assertEquals(0, columnFamilies[0].getScope());
+                assertEquals("CF", columnFamilies[1].getNameAsString());
+                assertEquals(false, columnFamilies[1].isInMemory());
+                assertEquals(1, columnFamilies[1].getScope());
+            }
+
+        } finally {
+            conn.close();
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
index cd882cb..d4ee4cc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
@@ -80,7 +80,8 @@ public abstract class MetaDataProtocol extends MetaDataService {
         NEWER_TABLE_FOUND,
         UNALLOWED_TABLE_MUTATION,
         NO_PK_COLUMNS,
-        PARENT_TABLE_NOT_FOUND
+        PARENT_TABLE_NOT_FOUND,
+        NO_OP
     };
 
   public static class MetaDataMutationResult {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
index f0fbf1d..fa44835 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServices.java
@@ -17,12 +17,11 @@
  */
 package org.apache.phoenix.query;
 
-import java.io.IOException;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.concurrent.TimeoutException;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.HTableDescriptor;
@@ -73,7 +72,7 @@ public interface ConnectionQueryServices extends QueryServices, MetaDataMutated
     public MetaDataMutationResult getTable(PName tenantId, byte[] schemaName, byte[] tableName, long tableTimestamp, long clientTimetamp) throws SQLException;
     public MetaDataMutationResult createTable(List<Mutation> tableMetaData, byte[] tableName, PTableType tableType, Map<String,Object> tableProps, List<Pair<byte[],Map<String,Object>>> families, byte[][] splits) throws SQLException;
     public MetaDataMutationResult dropTable(List<Mutation> tableMetadata, PTableType tableType, boolean cascade) throws SQLException;
-    public MetaDataMutationResult addColumn(List<Mutation> tableMetaData, List<Pair<byte[],Map<String,Object>>> families, PTable table) throws SQLException;
+    public MetaDataMutationResult addColumn(List<Mutation> tableMetaData, PTable table, Map<String, List<Pair<String,Object>>> properties, Set<String> colFamiliesForPColumnsToBeAdded) throws SQLException;
     public MetaDataMutationResult dropColumn(List<Mutation> tableMetadata, PTableType tableType) throws SQLException;
     public MetaDataMutationResult updateIndexState(List<Mutation> tableMetadata, String parentTableName) throws SQLException;
     public MutationState updateData(MutationPlan plan) throws SQLException;
@@ -112,5 +111,4 @@ public interface ConnectionQueryServices extends QueryServices, MetaDataMutated
     
     public void clearCache() throws SQLException;
     public int getSequenceSaltBuckets();
-    public void modifyTable(byte[] tableName, HTableDescriptor newDesc) throws IOException, InterruptedException, TimeoutException;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 870baa7..dcf3753 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -17,12 +17,16 @@
  */
 package org.apache.phoenix.query;
 
+import static org.apache.hadoop.hbase.HColumnDescriptor.TTL;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME_BYTES;
 import static org.apache.phoenix.query.QueryServicesOptions.DEFAULT_DROP_METADATA;
 
 import java.io.IOException;
 import java.sql.SQLException;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -107,13 +111,12 @@ import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.jdbc.PhoenixEmbeddedDriver.ConnectionInfo;
 import org.apache.phoenix.protobuf.ProtobufUtil;
+import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
 import org.apache.phoenix.schema.EmptySequenceCacheException;
 import org.apache.phoenix.schema.MetaDataSplitPolicy;
 import org.apache.phoenix.schema.NewerTableAlreadyExistsException;
-import org.apache.phoenix.schema.types.PBoolean;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PColumnFamily;
-import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.PMetaData;
 import org.apache.phoenix.schema.PMetaDataImpl;
 import org.apache.phoenix.schema.PName;
@@ -127,10 +130,12 @@ import org.apache.phoenix.schema.Sequence;
 import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.TableAlreadyExistsException;
 import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.schema.TableProperty;
 import org.apache.phoenix.schema.stats.PTableStats;
 import org.apache.phoenix.schema.stats.StatisticsUtil;
+import org.apache.phoenix.schema.types.PBoolean;
+import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PLong;
-import org.apache.phoenix.schema.types.PUnsignedTinyint;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.Closeables;
 import org.apache.phoenix.util.ConfigUtil;
@@ -194,7 +199,9 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
     // setting this member variable guarded by "connectionCountLock"
     private volatile ConcurrentMap<SequenceKey,Sequence> sequenceMap = Maps.newConcurrentMap();
     private KeyValueBuilder kvBuilder;
-
+    
+    private static final HColumnDescriptor defaultColDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES);
+    
     private PMetaData newEmptyMetaData() {
         long maxSizeBytes = props.getLong(QueryServices.MAX_CLIENT_METADATA_CACHE_SIZE_ATTRIB,
                 QueryServicesOptions.DEFAULT_MAX_CLIENT_METADATA_CACHE_SIZE);
@@ -400,8 +407,8 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                 List<HRegionLocation> locations = Lists.newArrayList();
                 byte[] currentKey = HConstants.EMPTY_START_ROW;
                 do {
-                HRegionLocation regionLocation = connection.getRegionLocation(
-                    TableName.valueOf(tableName), currentKey, reload);
+                  HRegionLocation regionLocation = connection.getRegionLocation(
+                      TableName.valueOf(tableName), currentKey, reload);
                   locations.add(regionLocation);
                   currentKey = regionLocation.getRegionInfo().getEndKey();
                 } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW));
@@ -563,14 +570,22 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         return columnDesc;
     }
 
-    private void modifyColumnFamilyDescriptor(HColumnDescriptor hcd, Pair<byte[],Map<String,Object>> family) throws SQLException {
-      for (Entry<String, Object> entry : family.getSecond().entrySet()) {
-        String key = entry.getKey();
-        Object value = entry.getValue();
-        hcd.setValue(key, value == null ? null : value.toString());
-      }
+    private void modifyColumnFamilyDescriptor(HColumnDescriptor hcd, Pair<byte[], Map<String,Object>> family) throws SQLException {
+        if (Bytes.equals(hcd.getName(), family.getFirst())) {
+            modifyColumnFamilyDescriptor(hcd, family.getSecond());
+        } else {
+            throw new IllegalArgumentException("Column family names don't match. Column descriptor family name: " + hcd.getNameAsString() + ", Family name: " + Bytes.toString(family.getFirst()));
+        }
     }
-
+    
+    private void modifyColumnFamilyDescriptor(HColumnDescriptor hcd, Map<String,Object> props) throws SQLException {
+        for (Entry<String, Object> entry : props.entrySet()) {
+            String propName = entry.getKey();
+            Object value = entry.getValue();
+            hcd.setValue(propName, value == null ? null : value.toString());
+        }
+    }
+    
     private HTableDescriptor generateTableDescriptor(byte[] tableName, HTableDescriptor existingDesc, PTableType tableType, Map<String,Object> tableProps, List<Pair<byte[],Map<String,Object>>> families, byte[][] splits) throws SQLException {
         String defaultFamilyName = (String)tableProps.remove(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME);
         HTableDescriptor tableDescriptor = (existingDesc != null) ? new HTableDescriptor(existingDesc) :
@@ -680,93 +695,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         }
     }
 
-    private void ensureFamilyCreated(byte[] tableName, PTableType tableType , Pair<byte[],Map<String,Object>> family) throws SQLException {
-        HBaseAdmin admin = null;
-        SQLException sqlE = null;
-        try {
-            admin = new HBaseAdmin(config);
-            try {
-                HTableDescriptor existingTableDesc = admin.getTableDescriptor(tableName);
-                HColumnDescriptor oldColumnDesc = existingTableDesc.getFamily(family.getFirst());
-                HColumnDescriptor newColumnDesc = null;
-
-                if (oldColumnDesc == null) {
-                    if (tableType == PTableType.VIEW) {
-                        String fullTableName = Bytes.toString(tableName);
-                        throw new ReadOnlyTableException(
-                                "The HBase column families for a VIEW must already exist",
-                                SchemaUtil.getSchemaNameFromFullName(fullTableName),
-                                SchemaUtil.getTableNameFromFullName(fullTableName),
-                                Bytes.toString(family.getFirst()));
-                    }
-                    newColumnDesc = generateColumnFamilyDescriptor(family, tableType );
-                } else {
-                    newColumnDesc = new HColumnDescriptor(oldColumnDesc);
-                    // Don't attempt to make any metadata changes for a VIEW
-                    if (tableType == PTableType.VIEW) {
-                        return;
-                    }
-                    modifyColumnFamilyDescriptor(newColumnDesc, family);
-                }
-
-                if (newColumnDesc.equals(oldColumnDesc)) {
-                    // Table already has family and it's the same.
-                    return;
-                }
-                addOrModifyColumnDescriptor(tableName, admin, oldColumnDesc, newColumnDesc);
-            } catch (org.apache.hadoop.hbase.TableNotFoundException e) {
-                sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.TABLE_UNDEFINED).setRootCause(e).build().buildException();
-            } catch (InterruptedException e) {
-                // restore the interrupt status
-                Thread.currentThread().interrupt();
-                sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.INTERRUPTED_EXCEPTION).setRootCause(e).build().buildException();
-            } catch (TimeoutException e) {
-                sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.OPERATION_TIMED_OUT).setRootCause(e.getCause() != null ? e.getCause() : e).build().buildException();
-            }
-        } catch (IOException e) {
-            sqlE = ServerUtil.parseServerException(e);
-        } finally {
-            try {
-                if (admin != null) {
-                    admin.close();
-                }
-            } catch (IOException e) {
-                if (sqlE == null) {
-                    sqlE = ServerUtil.parseServerException(e);
-                } else {
-                    sqlE.setNextException(ServerUtil.parseServerException(e));
-                }
-            } finally {
-                if (sqlE != null) {
-                    throw sqlE;
-                }
-            }
-        }
-    }
-
-    private void addOrModifyColumnDescriptor(byte[] tableName, HBaseAdmin admin, HColumnDescriptor oldColumnDesc,
-            HColumnDescriptor newColumnDesc) throws IOException, InterruptedException, TimeoutException {
-        boolean isOnlineSchemaUpgradeEnabled = ConnectionQueryServicesImpl.this.props.getBoolean(
-                QueryServices.ALLOW_ONLINE_TABLE_SCHEMA_UPDATE,
-                QueryServicesOptions.DEFAULT_ALLOW_ONLINE_TABLE_SCHEMA_UPDATE);
-        if (!isOnlineSchemaUpgradeEnabled) {
-            admin.disableTable(tableName);
-            if (oldColumnDesc == null) {
-                admin.addColumn(tableName, newColumnDesc);
-            } else {
-                admin.modifyColumn(tableName, newColumnDesc);
-            }
-            admin.enableTable(tableName);
-        } else {
-            if (oldColumnDesc == null) {
-                admin.addColumn(tableName, newColumnDesc);
-            } else {
-                admin.modifyColumn(tableName, newColumnDesc);
-            }
-            pollForUpdatedColumnDescriptor(admin, tableName, newColumnDesc);
-        }
-    }
-
     private static interface RetriableOperation {
         boolean checkForCompletion() throws TimeoutException, IOException;
         String getOperatioName();
@@ -789,23 +717,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         });
     }
 
-    private void pollForUpdatedColumnDescriptor(final HBaseAdmin admin, final byte[] tableName,
-            final HColumnDescriptor columnFamilyDesc) throws InterruptedException, TimeoutException {
-        checkAndRetry(new RetriableOperation() {
-
-            @Override
-            public String getOperatioName() {
-                return "UpdateOrNewColumnDescriptor";
-            }
-
-            @Override
-            public boolean checkForCompletion() throws TimeoutException, IOException {
-                HTableDescriptor newTableDesc = admin.getTableDescriptor(tableName);
-                return newTableDesc.getFamilies().contains(columnFamilyDesc);
-            }
-        });
-    }
-
     private void checkAndRetry(RetriableOperation op) throws InterruptedException, TimeoutException {
         int maxRetries = ConnectionQueryServicesImpl.this.props.getInt(
                 QueryServices.NUM_RETRIES_FOR_SCHEMA_UPDATE_CHECK,
@@ -840,7 +751,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         if (!success) {
             throw new TimeoutException("Operation  " + op.getOperatioName() + " didn't complete within "
                     + watch.elapsedMillis() + " ms "
-                    + (numTries > 1 ? ("after retrying " + numTries + (numTries > 1 ? "times." : "time.")) : ""));
+                    + (numTries > 1 ? ("after trying " + numTries + (numTries > 1 ? "times." : "time.")) : ""));
         } else {
             if (logger.isDebugEnabled()) {
                 logger.debug("Operation "
@@ -848,7 +759,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                         + " completed within "
                         + watch.elapsedMillis()
                         + "ms "
-                        + (numTries > 1 ? ("after retrying " + numTries + (numTries > 1 ? "times." : "time.")) : ""));
+                        + (numTries > 1 ? ("after trying " + numTries + (numTries > 1 ? "times." : "time.")) : ""));
             }
         }
     }
@@ -938,7 +849,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                     return existingDesc;
                 }
 
-                modifyTable(tableName, newDesc);
+                modifyTable(tableName, newDesc, true);
                 return newDesc;
             }
 
@@ -970,19 +881,20 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         return null; // will never make it here
     }
 
-    @Override
-    public void modifyTable(byte[] tableName, HTableDescriptor newDesc) throws IOException, InterruptedException,
-            TimeoutException {
-        try (HBaseAdmin admin = new HBaseAdmin(config)) {
-            if (!allowOnlineTableSchemaUpdate()) {
-                admin.disableTable(tableName);
-                admin.modifyTable(tableName, newDesc);
-                admin.enableTable(tableName);
-            } else {
-                admin.modifyTable(tableName, newDesc);
-                pollForUpdatedTableDescriptor(admin, newDesc, tableName);
-            }
-        }
+    private void modifyTable(byte[] tableName, HTableDescriptor newDesc, boolean shouldPoll) throws IOException,
+    InterruptedException, TimeoutException {
+    	try (HBaseAdmin admin = new HBaseAdmin(config)) {
+    		if (!allowOnlineTableSchemaUpdate()) {
+    			admin.disableTable(tableName);
+    			admin.modifyTable(tableName, newDesc);
+    			admin.enableTable(tableName);
+    		} else {
+    			admin.modifyTable(tableName, newDesc);
+    			if (shouldPoll) {
+    			    pollForUpdatedTableDescriptor(admin, newDesc, tableName);
+    			}
+    		}
+    	}
     }
 
     private static boolean isInvalidMutableIndexConfig(Long serverVersion) {
@@ -1506,11 +1418,42 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
 
         ensureViewIndexTableCreated(physicalTableName, tableProps, families, splits, timestamp);
     }
-
+    
     @Override
-    public MetaDataMutationResult addColumn(final List<Mutation> tableMetaData, List<Pair<byte[],Map<String,Object>>> families, PTable table) throws SQLException {
-        byte[][] rowKeyMetaData = new byte[3][];
-        PTableType tableType = table.getType();
+    public MetaDataMutationResult addColumn(final List<Mutation> tableMetaData, PTable table, Map<String, List<Pair<String,Object>>> stmtProperties, Set<String> colFamiliesForPColumnsToBeAdded) throws SQLException {
+        List<Pair<byte[], Map<String, Object>>> families = new ArrayList<>(stmtProperties.size());
+        Map<String, Object> tableProps = new HashMap<String, Object>();
+        HTableDescriptor tableDescriptor = separateAndValidateProperties(table, stmtProperties, colFamiliesForPColumnsToBeAdded, families, tableProps);
+        SQLException sqlE = null;
+        if (tableDescriptor != null) {
+            try {
+                boolean pollingNotNeeded = (!tableProps.isEmpty() && families.isEmpty() && colFamiliesForPColumnsToBeAdded.isEmpty());
+                modifyTable(table.getPhysicalName().getBytes(), tableDescriptor, !pollingNotNeeded);
+            } catch (IOException e) {
+                sqlE = ServerUtil.parseServerException(e);
+            } catch (InterruptedException e) {
+                // restore the interrupt status
+                Thread.currentThread().interrupt();
+                sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.INTERRUPTED_EXCEPTION).setRootCause(e).build().buildException();
+            } catch (TimeoutException e) {
+                sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.OPERATION_TIMED_OUT).setRootCause(e.getCause() != null ? e.getCause() : e).build().buildException();
+            } finally {
+                if (sqlE != null) {
+                    throw sqlE;
+                }
+            }
+        }
+        
+        // Special case for call during drop table to ensure that the empty column family exists.
+        // In this, case we only include the table header row, as until we add schemaBytes and tableBytes
+        // as args to this function, we have no way of getting them in this case.
+        // TODO: change to  if (tableMetaData.isEmpty()) once we pass through schemaBytes and tableBytes
+        // Also, could be used to update property values on ALTER TABLE t SET prop=xxx
+        if ((tableMetaData.isEmpty()) || (tableMetaData.size() == 1 && tableMetaData.get(0).isEmpty())) {
+            return new MetaDataMutationResult(MutationCode.NO_OP, System.currentTimeMillis(), table);
+        }
+         byte[][] rowKeyMetaData = new byte[3][];
+         PTableType tableType = table.getType();
 
         Mutation m = tableMetaData.get(0);
         byte[] rowKey = m.getRow();
@@ -1519,17 +1462,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         byte[] schemaBytes = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
         byte[] tableBytes = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
         byte[] tableKey = SchemaUtil.getTableKey(tenantIdBytes, schemaBytes, tableBytes);
-        for ( Pair<byte[],Map<String,Object>>  family : families) {
-            ensureFamilyCreated(table.getPhysicalName().getBytes(), tableType, family);
-        }
-        // Special case for call during drop table to ensure that the empty column family exists.
-        // In this, case we only include the table header row, as until we add schemaBytes and tableBytes
-        // as args to this function, we have no way of getting them in this case.
-        // TODO: change to  if (tableMetaData.isEmpty()) once we pass through schemaBytes and tableBytes
-        // Also, could be used to update property values on ALTER TABLE t SET prop=xxx
-        if (tableMetaData.size() == 1 && tableMetaData.get(0).isEmpty()) {
-            return null;
-        }
+        
         ImmutableBytesWritable ptr = new ImmutableBytesWritable();
         MetaDataMutationResult result =  metaDataCoprocessorExec(tableKey,
             new Batch.Call<MetaDataService, MetaDataResponse>() {
@@ -1573,7 +1506,207 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
         }
         return result;
     }
+    
+    private HTableDescriptor separateAndValidateProperties(PTable table, Map<String, List<Pair<String, Object>>> properties, Set<String> colFamiliesForPColumnsToBeAdded, List<Pair<byte[], Map<String, Object>>> families, Map<String, Object> tableProps) throws SQLException {
+        Map<String, Map<String, Object>> stmtFamiliesPropsMap = new HashMap<>(properties.size());
+        Map<String,Object> commonFamilyProps = new HashMap<>();
+        boolean addingColumns = colFamiliesForPColumnsToBeAdded != null && colFamiliesForPColumnsToBeAdded.size() > 0;
+        HashSet<String> existingColumnFamilies = existingColumnFamilies(table);
+        Map<String, Map<String, Object>> allFamiliesProps = new HashMap<>(existingColumnFamilies.size());
+        for (String family : properties.keySet()) {
+            List<Pair<String, Object>> propsList = properties.get(family);
+            if (propsList != null && propsList.size() > 0) {
+                Map<String, Object> colFamilyPropsMap = new HashMap<String, Object>(propsList.size());
+                for (Pair<String, Object> prop : propsList) {
+                    String propName = prop.getFirst();
+                    Object propValue = prop.getSecond();
+                    if ((isHTableProperty(propName) ||  TableProperty.isPhoenixTableProperty(propName)) && addingColumns) {
+                        // setting HTable and PhoenixTable properties while adding a column is not allowed.
+                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN)
+                        .setMessage("Property: " + propName).build()
+                        .buildException();
+                    }
+                    if (isHTableProperty(propName)) {
+                        // Can't have a column family name for a property that's an HTableProperty
+                        if (!family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
+                            throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY)
+                            .setMessage("Column Family: " + family + ", Property: " + propName).build()
+                            .buildException(); 
+                        }
+                        tableProps.put(propName, propValue);
+                    } else {
+                        if (TableProperty.isPhoenixTableProperty(propName)) {
+                            TableProperty.valueOf(propName).validate(true, !family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY), table.getType());
+                            if (propName.equals(TTL)) {
+                                // Even though TTL is really a HColumnProperty we treat it specially.
+                                // We enforce that all column families have the same TTL.
+                                commonFamilyProps.put(propName, prop.getSecond());
+                            }
+                        } else {
+                            if (isHColumnProperty(propName)) {
+                                if (family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
+                                    commonFamilyProps.put(propName, prop.getSecond());
+                                } else {
+                                    colFamilyPropsMap.put(propName, prop.getSecond());
+                                }
+                            } else {
+                                // invalid property - neither of HTableProp, HColumnProp or PhoenixTableProp
+                                // FIXME: This isn't getting triggered as currently a property gets evaluated 
+                                // as HTableProp if its neither HColumnProp or PhoenixTableProp.
+                                throw new SQLExceptionInfo.Builder(SQLExceptionCode.SET_UNSUPPORTED_PROP_ON_ALTER_TABLE)
+                                .setMessage("Column Family: " + family + ", Property: " + propName).build()
+                                .buildException();
+                            }
+                        }
+                    }
+                }
+                if (!colFamilyPropsMap.isEmpty()) {
+                    stmtFamiliesPropsMap.put(family, colFamilyPropsMap);
+                }
+  
+            }
+        }
+        commonFamilyProps = Collections.unmodifiableMap(commonFamilyProps);
+        boolean isAddingPkColOnly = colFamiliesForPColumnsToBeAdded.size() == 1 && colFamiliesForPColumnsToBeAdded.contains(null);
+        if (!commonFamilyProps.isEmpty()) {
+            if (!addingColumns) {
+                // Add the common family props to all existing column families
+                for (String existingColFamily : existingColumnFamilies) {
+                    Map<String, Object> m = new HashMap<String, Object>(commonFamilyProps.size());
+                    m.putAll(commonFamilyProps);
+                    allFamiliesProps.put(existingColFamily, m);
+                }
+            } else {
+                // Add the common family props to the column families of the columns being added
+                for (String colFamily : colFamiliesForPColumnsToBeAdded) {
+                    if (colFamily != null) {
+                        // only set properties for key value columns
+                        Map<String, Object> m = new HashMap<String, Object>(commonFamilyProps.size());
+                        m.putAll(commonFamilyProps);
+                        allFamiliesProps.put(colFamily, m);
+                    } else if (isAddingPkColOnly) {
+                        // Setting HColumnProperty for a pk column is invalid 
+                        // because it will be part of the row key and not a key value column family.
+                        // However, if both pk cols as well as key value columns are getting added 
+                        // together, then its allowed. The above if block will make sure that we add properties
+                        // only for the kv cols and not pk cols.
+                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.SET_UNSUPPORTED_PROP_ON_ALTER_TABLE)
+                                .build().buildException();
+                    }
+                }
+            }
+        }
+
+        // Now go through the column family properties specified in the statement
+        // and merge them with the common family properties.
+        for (String f : stmtFamiliesPropsMap.keySet()) {
+            if (!addingColumns && !existingColumnFamilies.contains(f)) {
+                throw new ColumnFamilyNotFoundException(f);
+            }
+            if (addingColumns && !colFamiliesForPColumnsToBeAdded.contains(f)) {
+                throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SET_PROPERTY_FOR_COLUMN_NOT_ADDED).build().buildException();
+            }
+            Map<String, Object> commonProps = allFamiliesProps.get(f);
+            Map<String, Object> stmtProps = stmtFamiliesPropsMap.get(f);
+            if (commonProps != null) {
+                if (stmtProps != null) {
+                    // merge common props with statement props for the family
+                    commonProps.putAll(stmtProps);
+                }
+            } else {
+                // if no common props were specified, then assign family specific props
+                if (stmtProps != null) {
+                    allFamiliesProps.put(f, stmtProps);
+                }
+            }
+        }
+
+        // case when there is a column family being added but there are no props
+        // For ex - in DROP COLUMN when a new empty CF needs to be added since all 
+        // the columns of the existing empty CF are getting dropped. Or the case 
+        // when one is just adding a column for a column family like this:
+        // ALTER TABLE ADD CF.COL
+        for (String cf : colFamiliesForPColumnsToBeAdded) {
+            if (cf != null && allFamiliesProps.get(cf) == null) {
+                allFamiliesProps.put(cf, new HashMap<String, Object>());
+            }
+        }
+
+        if (table.getColumnFamilies().isEmpty() && !addingColumns && !commonFamilyProps.isEmpty()) {
+            allFamiliesProps.put(Bytes.toString(table.getDefaultFamilyName() == null ? QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES : table.getDefaultFamilyName().getBytes() ), commonFamilyProps);
+        }
 
+
+        // Views are not allowed to have any of these properties.
+        if (table.getType() == PTableType.VIEW && (!stmtFamiliesPropsMap.isEmpty() || !commonFamilyProps.isEmpty() || !tableProps.isEmpty())) {
+            throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES).build()
+            .buildException();
+        }
+        HTableDescriptor newTableDescriptor = null;
+        if (!allFamiliesProps.isEmpty() || !tableProps.isEmpty()) {
+            byte[] tableNameBytes = Bytes.toBytes(table.getPhysicalName().getString());
+            HTableDescriptor existingTableDescriptor = getTableDescriptor(tableNameBytes);
+            newTableDescriptor = new HTableDescriptor(existingTableDescriptor);
+            if (!tableProps.isEmpty()) {
+                // add all the table properties to the existing table descriptor
+                for (Entry<String, Object> entry : tableProps.entrySet()) {
+                    newTableDescriptor.setValue(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : null);
+                }
+            }
+            if (addingColumns) {
+                // Make sure that all the CFs of the table have the same TTL as the empty CF. 
+                setTTLToEmptyCFTTL(allFamiliesProps, table, newTableDescriptor);
+            }
+            for (Entry<String, Map<String, Object>> entry : allFamiliesProps.entrySet()) {
+                byte[] cf = entry.getKey().getBytes();
+                HColumnDescriptor colDescriptor = newTableDescriptor.getFamily(cf);
+                if (colDescriptor == null) {
+                    // new column family
+                    colDescriptor = generateColumnFamilyDescriptor(new Pair<>(cf, entry.getValue()), table.getType());
+                    newTableDescriptor.addFamily(colDescriptor);
+                } else {
+                    modifyColumnFamilyDescriptor(colDescriptor, entry.getValue());
+                }
+            }
+        }
+        return newTableDescriptor;
+    }
+    
+    private boolean isHColumnProperty(String propName) {
+        return defaultColDescriptor.getValue(propName) != null;
+    }
+
+    private boolean isHTableProperty(String propName) {
+        return !isHColumnProperty(propName) && !TableProperty.isPhoenixTableProperty(propName);
+    } 
+    
+    private HashSet<String> existingColumnFamilies(PTable table) {
+        List<PColumnFamily> cfs = table.getColumnFamilies();
+        HashSet<String> cfNames = new HashSet<>(cfs.size());
+        for (PColumnFamily cf : table.getColumnFamilies()) {
+            cfNames.add(cf.getName().getString());
+        }
+        return cfNames;
+    }
+
+    private int getTTLForEmptyCf(byte[] emptyCf, byte[] tableNameBytes, HTableDescriptor tableDescriptor) throws SQLException {
+        if (tableDescriptor == null) {
+            tableDescriptor = getTableDescriptor(tableNameBytes);
+        }
+        return tableDescriptor.getFamily(emptyCf).getTimeToLive();
+    }
+    
+    private void setTTLToEmptyCFTTL(Map<String, Map<String, Object>> familyProps, PTable table, 
+            HTableDescriptor tableDesc) throws SQLException {
+        if (!familyProps.isEmpty()) {
+            int emptyCFTTL = getTTLForEmptyCf(SchemaUtil.getEmptyColumnFamily(table), table.getPhysicalName().getBytes(), tableDesc);
+            for (String family : familyProps.keySet()) {
+                Map<String, Object> props = familyProps.get(family) != null ? familyProps.get(family) : new HashMap<String, Object>();
+                props.put(TTL, emptyCFTTL);
+            }
+        }
+    }
+    
     @Override
     public MetaDataMutationResult dropColumn(final List<Mutation> tableMetaData, PTableType tableType) throws SQLException {
         byte[][] rowKeyMetadata = new byte[3][];
@@ -1688,14 +1821,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement
                                 // Ignore, as this will happen if the SYSTEM.CATALOG already exists at this fixed timestamp.
                                 // A TableAlreadyExistsException is not thrown, since the table only exists *after* this fixed timestamp.
                             } catch (TableAlreadyExistsException ignore) {
-                                // This will occur if we have an older SYSTEM.CATALOG and we need to update it to include
-                                // any new columns we've added.
-                                metaConnection = addColumnsIfNotExists(metaConnection,
-                                  PhoenixDatabaseMetaData.SYSTEM_CATALOG,
-                                  MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP,
-                                  PhoenixDatabaseMetaData.INDEX_TYPE + " " + PUnsignedTinyint.INSTANCE.getSqlTypeName() +
-                                  ", " + PhoenixDatabaseMetaData.INDEX_DISABLE_TIMESTAMP + " " + PLong.INSTANCE.getSqlTypeName() +
-                                          ", " + PhoenixDatabaseMetaData.STORE_NULLS + " " + PBoolean.INSTANCE.getSqlTypeName());
                             }
                             int nSaltBuckets = ConnectionQueryServicesImpl.this.props.getInt(QueryServices.SEQUENCE_SALT_BUCKETS_ATTRIB,
                                     QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
index da893e6..434f3eb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionlessQueryServicesImpl.java
@@ -19,13 +19,12 @@ package org.apache.phoenix.query;
 
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.INDEX_STATE_BYTES;
 
-import java.io.IOException;
 import java.sql.SQLException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.concurrent.TimeoutException;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
@@ -223,7 +222,7 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
     }
 
     @Override
-    public MetaDataMutationResult addColumn(List<Mutation> tableMetaData, List<Pair<byte[],Map<String,Object>>> families, PTable table) throws SQLException {
+    public MetaDataMutationResult addColumn(List<Mutation> tableMetaData, PTable table, Map<String, List<Pair<String,Object>>> properties, Set<String> colFamiliesForPColumnsToBeAdded) throws SQLException {
         return new MetaDataMutationResult(MutationCode.TABLE_ALREADY_EXISTS, 0, null);
     }
 
@@ -480,9 +479,5 @@ public class ConnectionlessQueryServicesImpl extends DelegateQueryServices imple
         return getProps().getInt(QueryServices.SEQUENCE_SALT_BUCKETS_ATTRIB,
                 QueryServicesOptions.DEFAULT_SEQUENCE_TABLE_SALT_BUCKETS);
     }
-
-    @Override
-    public void modifyTable(byte[] tableName, HTableDescriptor newDesc) throws IOException,
-            InterruptedException, TimeoutException {
-    } 
+ 
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
index a76738e..9a33fa3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java
@@ -17,12 +17,11 @@
  */
 package org.apache.phoenix.query;
 
-import java.io.IOException;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.concurrent.TimeoutException;
+import java.util.Set;
 
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.HTableDescriptor;
@@ -118,8 +117,8 @@ public class DelegateConnectionQueryServices extends DelegateQueryServices imple
     }
 
     @Override
-    public MetaDataMutationResult addColumn(List<Mutation> tabeMetaData, List<Pair<byte[],Map<String,Object>>> families, PTable table) throws SQLException {
-        return getDelegate().addColumn(tabeMetaData, families, table);
+    public MetaDataMutationResult addColumn(List<Mutation> tableMetaData, PTable table, Map<String, List<Pair<String,Object>>> properties, Set<String> colFamiliesForPColumnsToBeAdded) throws SQLException {
+        return getDelegate().addColumn(tableMetaData, table, properties, colFamiliesForPColumnsToBeAdded);
     }
 
 
@@ -251,10 +250,4 @@ public class DelegateConnectionQueryServices extends DelegateQueryServices imple
     public int getSequenceSaltBuckets() {
         return getDelegate().getSequenceSaltBuckets();
     }
-
-    @Override
-    public void modifyTable(byte[] tableName, HTableDescriptor newDesc) throws IOException,
-            InterruptedException, TimeoutException {
-        getDelegate().modifyTable(tableName, newDesc);
-    } 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index ea3af37..5fffbf2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -47,9 +47,9 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PARENT_TENANT_ID;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PHYSICAL_NAME;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.PK_NAME;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.REGION_NAME;
-import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.STORE_NULLS;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SALT_BUCKETS;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SORT_ORDER;
+import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.STORE_NULLS;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_NAME;
@@ -74,21 +74,17 @@ import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
 import java.sql.Types;
-import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Properties;
 import java.util.Set;
-import java.util.concurrent.TimeoutException;
 
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.HColumnDescriptor;
@@ -140,7 +136,6 @@ import org.apache.phoenix.parse.ParseNodeFactory;
 import org.apache.phoenix.parse.PrimaryKeyConstraint;
 import org.apache.phoenix.parse.TableName;
 import org.apache.phoenix.parse.UpdateStatisticsStatement;
-import org.apache.phoenix.query.ConnectionQueryServices;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServices;
 import org.apache.phoenix.query.QueryServicesOptions;
@@ -159,7 +154,6 @@ import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.PhoenixRuntime;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.SchemaUtil;
-import org.apache.phoenix.util.ServerUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -168,6 +162,7 @@ import com.google.common.collect.Iterators;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import com.google.common.primitives.Ints;
 
 public class MetaDataClient {
@@ -276,9 +271,6 @@ public class MetaDataClient {
 
     private final PhoenixConnection connection;
 
-    private static final HColumnDescriptor defaultColDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES);
-
-
     public MetaDataClient(PhoenixConnection connection) {
         this.connection = connection;
     }
@@ -1881,6 +1873,7 @@ public class MetaDataClient {
             }
             throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
                 .setSchemaName(schemaName).setTableName(tableName).setFamilyName(familyName).setColumnName(columnName).setMessage(msg).build().buildException();
+        case NO_OP:
         case COLUMN_ALREADY_EXISTS:
         case COLUMN_NOT_FOUND:
             break;
@@ -1978,76 +1971,34 @@ public class MetaDataClient {
             Boolean disableWALProp = null;
             Boolean storeNullsProp = null;
 
-            // flatten, separate and validate properties
-            ListMultimap<String,Pair<String,Object>> stmtPropsMap = statement.getProps();
-            Map<String, Map<String, Object>> stmtFamiliesPropsMap = new HashMap<>(stmtPropsMap.size());
-            Map<String, Object> tableProps = new HashMap<>(stmtPropsMap.size());
+            ListMultimap<String,Pair<String,Object>> stmtProperties = statement.getProps();
+            Map<String, List<Pair<String, Object>>> properties = new HashMap<>(stmtProperties.size());
             PTable table = FromCompiler.getResolver(statement, connection).getTables().get(0).getTable();
-            Map<String,Object> commonFamilyProps = new HashMap<>();
             List<ColumnDef> columnDefs = statement.getColumnDefs();
             if (columnDefs == null) {
-                columnDefs = Lists.newArrayListWithExpectedSize(1);
+                columnDefs = Collections.emptyList();
             }
-            for (String family : stmtPropsMap.keySet()) {
-                List<Pair<String, Object>> propsList = stmtPropsMap.get(family);
-                Map<String, Object> colFamilyPropsMap = new HashMap<String, Object>(propsList.size());
+            for (String family : stmtProperties.keySet()) {
+                List<Pair<String, Object>> propsList = stmtProperties.get(family);
                 for (Pair<String, Object> prop : propsList) {
                     String propName = prop.getFirst();
-                    if ((isHTableProperty(propName) ||  TableProperty.isPhoenixTableProperty(propName)) && columnDefs.size() > 0) {
-                    	// setting HTable and PhoenixTable properties while adding a column is not allowed.
-                    	throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN)
-                    	.setMessage("Property: " + propName).build()
-                    	.buildException();
-                    }
-                    if (isHTableProperty(propName)) {
-                        // Can't have a column family name for a property that's an HTableProperty
-                        if (!family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
-                            throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY)
-                            .setMessage("Column Family: " + family + ", Property: " + propName).build()
-                            .buildException();
+                    if (TableProperty.isPhoenixTableProperty(propName)) {
+                        TableProperty.valueOf(propName).validate(true, !family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY), table.getType());
+                        if (propName.equals(PTable.IS_IMMUTABLE_ROWS_PROP_NAME)) {
+                            isImmutableRowsProp = (Boolean)prop.getSecond();
+                        } else if (propName.equals(PhoenixDatabaseMetaData.MULTI_TENANT)) {
+                            multiTenantProp = (Boolean)prop.getSecond();
+                        } else if (propName.equals(DISABLE_WAL)) {
+                            disableWALProp = (Boolean)prop.getSecond();
+                        } else if (propName.equals(STORE_NULLS)) {
+                            storeNullsProp = (Boolean)prop.getSecond();
                         }
-                        tableProps.put(propName, prop.getSecond());
-                    } else {
-                        if (TableProperty.isPhoenixTableProperty(propName)) {
-                        	TableProperty.valueOf(propName).validate(true, !family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY), table.getType());
-                            if (propName.equals(TTL)) {
-                                // Even though TTL is really a HColumnProperty we treat it specially.
-                                // We enforce that all column families have the same TTL.
-                            	commonFamilyProps.put(propName, prop.getSecond());
-                            } else if (propName.equals(PTable.IS_IMMUTABLE_ROWS_PROP_NAME)) {
-                                isImmutableRowsProp = (Boolean)prop.getSecond();
-                            } else if (propName.equals(PhoenixDatabaseMetaData.MULTI_TENANT)) {
-                                multiTenantProp = (Boolean)prop.getSecond();
-                            } else if (propName.equals(DISABLE_WAL)) {
-                                disableWALProp = (Boolean)prop.getSecond();
-                            } else if (propName.equals(STORE_NULLS)) {
-                                storeNullsProp = (Boolean) prop.getSecond();
-                            }
-                        } else {
-                            if (isHColumnProperty(propName)) {
-                                if (family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
-                                    commonFamilyProps.put(propName, prop.getSecond());
-                                } else {
-                                    colFamilyPropsMap.put(propName, prop.getSecond());
-                                }
-                            } else {
-                                // invalid property - neither of HTableProp, HColumnProp or PhoenixTableProp
-                                // FIXME: This isn't getting triggered as currently a property gets evaluated
-                                // as HTableProp if its neither HColumnProp or PhoenixTableProp.
-                                throw new SQLExceptionInfo.Builder(SQLExceptionCode.SET_UNSUPPORTED_PROP_ON_ALTER_TABLE)
-                                .setMessage("Column Family: " + family + ", Property: " + propName).build()
-                                .buildException();
-                            }
-                        }
-                    }
-                }
-                if (!colFamilyPropsMap.isEmpty()) {
-                    stmtFamiliesPropsMap.put(family, colFamilyPropsMap);
+                    } 
                 }
+                properties.put(family, propsList);
             }
-
-
             boolean retried = false;
+            boolean changingPhoenixTableProperty = false;
             while (true) {
                 ColumnResolver resolver = FromCompiler.getResolver(statement, connection);
                 table = resolver.getTables().get(0).getTable();
@@ -2075,24 +2026,28 @@ public class MetaDataClient {
                 if (isImmutableRowsProp != null) {
                     if (isImmutableRowsProp.booleanValue() != table.isImmutableRows()) {
                         isImmutableRows = isImmutableRowsProp;
+                        changingPhoenixTableProperty = true;
                     }
                 }
                 Boolean multiTenant = null;
                 if (multiTenantProp != null) {
                     if (multiTenantProp.booleanValue() != table.isMultiTenant()) {
                         multiTenant = multiTenantProp;
+                        changingPhoenixTableProperty = true;
                     }
                 }
                 Boolean disableWAL = null;
                 if (disableWALProp != null) {
                     if (disableWALProp.booleanValue() != table.isWALDisabled()) {
                         disableWAL = disableWALProp;
+                        changingPhoenixTableProperty = true;
                     }
                 }
                 Boolean storeNulls = null;
                 if (storeNullsProp != null) {
                     if (storeNullsProp.booleanValue() != table.getStoreNulls()) {
                         storeNulls = storeNullsProp;
+                        changingPhoenixTableProperty = true;
                     }
                 }
 
@@ -2101,7 +2056,7 @@ public class MetaDataClient {
 
                 List<PColumn> columns = Lists.newArrayListWithExpectedSize(columnDefs.size());
                 Set<String> colFamiliesForPColumnsToBeAdded = new LinkedHashSet<>();
-                HashSet<String> existingColumnFamilies = existingColumnFamilies(table);
+                Set<String> families = new LinkedHashSet<>();
                 if (columnDefs.size() > 0 ) {
                     short nextKeySeq = SchemaUtil.getMaxKeySeq(table);
                     for( ColumnDef colDef : columnDefs) {
@@ -2126,8 +2081,9 @@ public class MetaDataClient {
                             pkName = table.getPKName() == null ? null : table.getPKName().getString();
                             keySeq = ++nextKeySeq;
                         } else {
-                            colFamiliesForPColumnsToBeAdded.add(column.getFamilyName().getString());
+                            families.add(column.getFamilyName().getString());
                         }
+                        colFamiliesForPColumnsToBeAdded.add(column.getFamilyName() == null ? null : column.getFamilyName().getString());
                         addColumnMutation(schemaName, tableName, column, colUpsert, null, pkName, keySeq, table.getBucketNum() != null);
                     }
 
@@ -2175,15 +2131,17 @@ public class MetaDataClient {
                     tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond());
                     connection.rollback();
                 }
-                long seqNum = incrementTableSeqNum(table, statement.getTableType(), 1,
-                        isImmutableRows, disableWAL, multiTenant, storeNulls);
-
-                tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond());
-                connection.rollback();
+                long seqNum = table.getSequenceNumber();
+                if (changingPhoenixTableProperty || columnDefs.size() > 0) { 
+                    seqNum = incrementTableSeqNum(table, statement.getTableType(), 1, isImmutableRows, disableWAL, multiTenant, storeNulls);
+                    tableMetaData.addAll(connection.getMutationState().toMutations().next().getSecond());
+                    connection.rollback();
+                }
+                
                 // Force the table header row to be first
                 Collections.reverse(tableMetaData);
 
-                byte[] family = colFamiliesForPColumnsToBeAdded.size() > 0 ? colFamiliesForPColumnsToBeAdded.iterator().next().getBytes() : null;
+                byte[] family = families.size() > 0 ? families.iterator().next().getBytes() : null;
 
                 // Figure out if the empty column family is changing as a result of adding the new column
                 byte[] emptyCF = null;
@@ -2201,78 +2159,7 @@ public class MetaDataClient {
                     }
                 }
 
-                Map<String, Map<String, Object>> allFamiliesProps = new HashMap<>(existingColumnFamilies.size() + stmtFamiliesPropsMap.size());
-                commonFamilyProps = Collections.unmodifiableMap(commonFamilyProps);
-                if (columnDefs.size() == 0) {
-                	// Add the common family props to all existing column families
-                	for (String existingColFamily : existingColumnFamilies) {
-                		Map<String, Object> m = new HashMap<String, Object>(commonFamilyProps.size());
-                		m.putAll(commonFamilyProps);
-                		allFamiliesProps.put(existingColFamily, m);
-                	}
-                } else {
-                	// Add the common family props to the column families of the columns being added
-                	for (String colFamily : colFamiliesForPColumnsToBeAdded) {
-                		Map<String, Object> m = new HashMap<String, Object>(commonFamilyProps.size());
-                		m.putAll(commonFamilyProps);
-                		allFamiliesProps.put(colFamily, m);
-                	}
-                }
-
-                if (table.getColumnFamilies().isEmpty() && columnDefs.size() == 0 && !commonFamilyProps.isEmpty()) {
-                	allFamiliesProps.put(Bytes.toString(table.getDefaultFamilyName() == null ? QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES : table.getDefaultFamilyName().getBytes() ), commonFamilyProps);
-                }
-
-                // Now go through the column family properties specified in the statement
-                // and merge them with the common family properties.
-                for (String f : stmtFamiliesPropsMap.keySet()) {
-                    if (allFamiliesProps.get(f) != null) {
-                        Map<String, Object> props = allFamiliesProps.get(f);
-                        Map<String, Object> stmtProps = stmtFamiliesPropsMap.get(f);
-                        props.putAll(stmtProps);
-                    } else {
-                    	if (columnDefs.size() == 0) {
-                    		throw new ColumnFamilyNotFoundException(f);
-                    	} else {
-                    		if (!existingColumnFamilies.contains(f)) {
-                    			throw new ColumnFamilyNotFoundException(f);
-                    		} else {
-                    			// If the family already exists then an attempt was made to
-                        		// add property for a column family that doesn't have a column
-                        		// being added in the statement.
-                    			throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SET_PROPERTY_FOR_COLUMN_NOT_ADDED).build()
-                                .buildException();
-                    		}
-                    	}
-                    }
-                }
-
-                // Views are not allowed to have any of these properties.
-                if (table.getType() == PTableType.VIEW && (!stmtFamiliesPropsMap.isEmpty() || !commonFamilyProps.isEmpty() || !tableProps.isEmpty())) {
-                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES).build()
-                    .buildException();
-                }
-
-                HTableDescriptor newTableDesc = null;
-                if (!tableProps.isEmpty()) {
-                	newTableDesc = modifyTableProps(tableName, table, tableProps);
-                }
-
-                if (columnDefs.size() > 0) {
-                	// Make sure that all the CFs of the table have the same TTL as the empty CF.
-                	setTTLToEmptyCFTTL(allFamiliesProps, table, newTableDesc);
-                }
-
-                List<Pair<byte[], Map<String, Object>>> families = new ArrayList<>(allFamiliesProps.size());
-                for (Entry<String, Map<String, Object>> entry : allFamiliesProps.entrySet()) {
-                    byte[] cf = entry.getKey().getBytes();
-                    Map<String, Object> props = entry.getValue();
-                    if (!props.isEmpty()) {
-                        families.add(new Pair<>(cf, props));
-                    }
-                }
-
-                MetaDataMutationResult result = connection.getQueryServices().addColumn(tableMetaData, families, table);
+                MetaDataMutationResult result = connection.getQueryServices().addColumn(tableMetaData, table, properties, colFamiliesForPColumnsToBeAdded);
                 try {
                     MutationCode code = processMutationResult(schemaName, tableName, result);
                     if (code == MutationCode.COLUMN_ALREADY_EXISTS) {
@@ -2336,48 +2223,6 @@ public class MetaDataClient {
         }
     }
 
-   private void setTTLToEmptyCFTTL(Map<String, Map<String, Object>> familyProps, PTable table,
-		   HTableDescriptor newTableDesc) throws SQLException {
-	   if (!familyProps.isEmpty()) {
-		   int emptyCFTTL = getTTLForEmptyCf(SchemaUtil.getEmptyColumnFamily(table), table.getPhysicalName().getBytes(), newTableDesc);
-		   for (String family : familyProps.keySet()) {
-			   Map<String, Object> props = familyProps.get(family) != null ? familyProps.get(family) : new HashMap<String, Object>();
-			   props.put(TTL, emptyCFTTL);
-		   }
-	   }
-   }
-
-    private HTableDescriptor modifyTableProps(String tableName, PTable table, Map<String, Object> tableProps) throws SQLException {
-    	byte[] tableNameBytes = Bytes.toBytes(tableName);
-    	ConnectionQueryServices services = connection.getQueryServices();
-    	HTableDescriptor existingTableDescriptor = services.getTableDescriptor(tableNameBytes);
-    	HTableDescriptor newTableDescriptor = new HTableDescriptor(existingTableDescriptor);
-    	// add all the table properties to the existing table descriptor
-    	for (Entry<String, Object> entry : tableProps.entrySet()) {
-    		newTableDescriptor.setValue(entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : null);
-    	}
-    	SQLException sqlE = null;
-    	try {
-    		services.modifyTable(tableNameBytes, newTableDescriptor);
-    		return newTableDescriptor;
-    	} catch (IOException e) {
-    		sqlE = ServerUtil.parseServerException(e);
-    		return null;
-    	} catch (InterruptedException e) {
-    		// restore the interrupt status
-    		Thread.currentThread().interrupt();
-    		sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.INTERRUPTED_EXCEPTION).setRootCause(e).build().buildException();
-    		return null;
-    	} catch (TimeoutException e) {
-    		sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.OPERATION_TIMED_OUT).setRootCause(e.getCause() != null ? e.getCause() : e).build().buildException();
-    		return null;
-    	} finally {
-    		if (sqlE != null) {
-    			throw sqlE;
-    		}
-    	}
-    }
-
     private String dropColumnMutations(PTable table, List<PColumn> columnsToDrop, List<Mutation> tableMetaData) throws SQLException {
         String tenantId = connection.getTenantId() == null ? "" : connection.getTenantId().getString();
         String schemaName = table.getSchemaName().getString();
@@ -2538,8 +2383,8 @@ public class MetaDataClient {
                             tableContainingColumnToDrop.getColumnFamily(emptyCF);
                         } catch (ColumnFamilyNotFoundException e) {
                             // Only if it's not already a column family do we need to ensure it's created
-                            List<Pair<byte[],Map<String,Object>>> family = Lists.newArrayListWithExpectedSize(1);
-                            family.add(new Pair<byte[],Map<String,Object>>(emptyCF,Collections.<String,Object>emptyMap()));
+                            Map<String, List<Pair<String,Object>>> family = new HashMap<>(1);
+                            family.put(Bytes.toString(emptyCF), Collections.<Pair<String, Object>>emptyList());
                             // Just use a Put without any key values as the Mutation, as addColumn will treat this specially
                             // TODO: pass through schema name and table name instead to these methods as it's cleaner
                             byte[] tenantIdBytes = connection.getTenantId() == null ? null : connection.getTenantId().getBytes();
@@ -2548,7 +2393,8 @@ public class MetaDataClient {
                                     Collections.<Mutation>singletonList(new Put(SchemaUtil.getTableKey
                                             (tenantIdBytes, tableContainingColumnToDrop.getSchemaName().getBytes(),
                                             tableContainingColumnToDrop.getTableName().getBytes()))),
-                                    family,tableContainingColumnToDrop);
+                                            tableContainingColumnToDrop, family, Sets.newHashSet(Bytes.toString(emptyCF)));
+
                         }
                     }
                 }
@@ -2740,34 +2586,4 @@ public class MetaDataClient {
         return table.getTableStats();
     }
 
-    private boolean isHColumnProperty(String propName) {
-    	return defaultColDescriptor.getValue(propName) != null;
-    }
-
-    private boolean isHTableProperty(String propName) {
-    	return !isHColumnProperty(propName) && !TableProperty.isPhoenixTableProperty(propName);
-    }
-
-    private HashSet<String> existingColumnFamilies(PTable table) {
-    	List<PColumnFamily> cfs = table.getColumnFamilies();
-    	HashSet<String> cfNames = new HashSet<>(cfs.size());
-    	for (PColumnFamily cf : table.getColumnFamilies()) {
-    		cfNames.add(cf.getName().getString());
-    	}
-    	return cfNames;
-    }
-
-    private int getTTLForEmptyCf(byte[] emptyCf, byte[] tableNameBytes, HTableDescriptor tableDescriptor) throws SQLException {
-    	if (tableDescriptor == null) {
-    		tableDescriptor = connection.getQueryServices().getTableDescriptor(tableNameBytes);
-    	}
-    	HColumnDescriptor[] colDescriptors = tableDescriptor.getColumnFamilies();
-    	for (HColumnDescriptor colDesc : colDescriptors) {
-    		if (Bytes.equals(emptyCf, colDesc.getName())) {
-    			return colDesc.getTimeToLive();
-    		}
-    	}
-    	throw new IllegalArgumentException("No match for empty column family found");
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/58c80a18/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index a2779a2..4db04ca 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -954,7 +954,7 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
             statement.execute();
             fail();
         } catch (SQLException e) { // expected
-            assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode());
+            assertEquals(SQLExceptionCode.SALT_ONLY_ON_CREATE_TABLE.getErrorCode(), e.getErrorCode());
         }
         try {
             PreparedStatement statement = conn.prepareStatement("ALTER TABLE atable SET SALT_BUCKETS=4");


Mime
View raw message