PHOENIX-2791 Support append only schema declaration
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/a0504fba Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/a0504fba Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/a0504fba Branch: refs/heads/master Commit: a0504fba12363eaa27ea3fd224671e92cb11a468 Parents: a8a3616 Author: Thomas D'Silva <tdsi...@salesforce.com> Authored: Thu May 5 15:38:18 2016 -0700 Committer: Thomas D'Silva <twdsi...@gmail.com> Committed: Tue May 10 15:10:25 2016 -0700 ---------------------------------------------------------------------- .../phoenix/end2end/AppendOnlySchemaIT.java | 330 +++++++++++++++++++ .../phoenix/end2end/AutoPartitionViewsIT.java | 6 +- .../apache/phoenix/compile/FromCompiler.java | 2 +- .../apache/phoenix/compile/JoinCompiler.java | 3 +- .../compile/TupleProjectionCompiler.java | 5 +- .../apache/phoenix/compile/UnionCompiler.java | 2 +- .../coprocessor/MetaDataEndpointImpl.java | 17 +- .../coprocessor/generated/PTableProtos.java | 103 +++++- .../phoenix/exception/SQLExceptionCode.java | 4 + .../phoenix/jdbc/PhoenixDatabaseMetaData.java | 3 + .../org/apache/phoenix/parse/ColumnDef.java | 7 +- .../query/ConnectionQueryServicesImpl.java | 26 +- .../apache/phoenix/query/QueryConstants.java | 2 + .../apache/phoenix/schema/DelegateTable.java | 6 + .../apache/phoenix/schema/MetaDataClient.java | 203 +++++++++--- .../java/org/apache/phoenix/schema/PTable.java | 9 +- .../org/apache/phoenix/schema/PTableImpl.java | 59 +++- .../apache/phoenix/schema/TableProperty.java | 3 +- .../org/apache/phoenix/util/MetaDataUtil.java | 12 +- .../phoenix/execute/CorrelatePlanTest.java | 2 +- .../execute/LiteralResultIteratorPlanTest.java | 2 +- phoenix-protocol/src/main/PTable.proto | 1 + 22 files changed, 706 insertions(+), 101 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/it/java/org/apache/phoenix/end2end/AppendOnlySchemaIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AppendOnlySchemaIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AppendOnlySchemaIT.java new file mode 100644 index 0000000..d764445 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AppendOnlySchemaIT.java @@ -0,0 +1,330 @@ +/* + * 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.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 static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyList; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import org.apache.hadoop.hbase.client.Mutation; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.jdbc.PhoenixConnection; +import org.apache.phoenix.jdbc.PhoenixEmbeddedDriver; +import org.apache.phoenix.query.ConnectionQueryServices; +import org.apache.phoenix.schema.PColumn; +import org.apache.phoenix.schema.PName; +import org.apache.phoenix.schema.PTable; +import org.apache.phoenix.schema.PTableKey; +import org.apache.phoenix.schema.PTableType; +import org.apache.phoenix.schema.TableAlreadyExistsException; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; +import org.mockito.Mockito; + +public class AppendOnlySchemaIT extends BaseHBaseManagedTimeIT { + + private void createTableWithSameSchema(boolean notExists, boolean sameClient) throws Exception { + // use a spyed ConnectionQueryServices so we can verify calls to getTable + ConnectionQueryServices connectionQueryServices = + Mockito.spy(driver.getConnectionQueryServices(getUrl(), + PropertiesUtil.deepCopy(TEST_PROPERTIES))); + Properties props = new Properties(); + props.putAll(PhoenixEmbeddedDriver.DEFFAULT_PROPS.asMap()); + + try (Connection conn1 = connectionQueryServices.connect(getUrl(), props); + Connection conn2 = sameClient ? conn1 : connectionQueryServices.connect(getUrl(), props)) { + // create sequence for auto partition + conn1.createStatement().execute("CREATE SEQUENCE metric_id_seq CACHE 1"); + // create base table + conn1.createStatement().execute("CREATE TABLE metric_table (metricId INTEGER NOT NULL, metricVal DOUBLE, CONSTRAINT PK PRIMARY KEY(metricId))" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=1, AUTO_PARTITION_SEQ=metric_id_seq"); + // create view + String ddl = + "CREATE VIEW " + (notExists ? "IF NOT EXISTS" : "") + + " view1( hostName varchar NOT NULL," + + " CONSTRAINT HOSTNAME_PK PRIMARY KEY (hostName))" + + " AS SELECT * FROM metric_table" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=300000"; + conn1.createStatement().execute(ddl); + conn1.createStatement().execute("UPSERT INTO view1(hostName, metricVal) VALUES('host1', 1.0)"); + conn1.commit(); + reset(connectionQueryServices); + + // execute same ddl + try { + conn2.createStatement().execute(ddl); + if (!notExists) { + fail("Create Table should fail"); + } + } + catch (TableAlreadyExistsException e) { + if (notExists) { + fail("Create Table should not fail"); + } + } + + // verify getTable rpcs + verify(connectionQueryServices, sameClient ? never() : atMost(1)).getTable((PName)isNull(), eq(new byte[0]), eq(Bytes.toBytes("VIEW1")), anyLong(), anyLong()); + + // verify create table rpcs + verify(connectionQueryServices, never()).createTable(anyListOf(Mutation.class), + any(byte[].class), any(PTableType.class), anyMap(), anyList(), any(byte[][].class), + eq(false)); + + // upsert one row + conn2.createStatement().execute("UPSERT INTO view1(hostName, metricVal) VALUES('host2', 2.0)"); + conn2.commit(); + // verify data in base table + ResultSet rs = conn2.createStatement().executeQuery("SELECT * from metric_table"); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(1.0, rs.getDouble(2), 1e-6); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(2.0, rs.getDouble(2), 1e-6); + assertFalse(rs.next()); + // verify data in view + rs = conn2.createStatement().executeQuery("SELECT * from view1"); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(1.0, rs.getDouble(2), 1e-6); + assertEquals("host1", rs.getString(3)); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(2.0, rs.getDouble(2), 1e-6); + assertEquals("host2", rs.getString(3)); + assertFalse(rs.next()); + } + } + + @Test + public void testSameSchemaWithNotExistsSameClient() throws Exception { + createTableWithSameSchema(true, true); + } + + @Test + public void testSameSchemaWithNotExistsDifferentClient() throws Exception { + createTableWithSameSchema(true, false); + } + + @Test + public void testSameSchemaSameClient() throws Exception { + createTableWithSameSchema(false, true); + } + + @Test + public void testSameSchemaDifferentClient() throws Exception { + createTableWithSameSchema(false, false); + } + + private void createTableAddColumns(boolean sameClient) throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn1 = DriverManager.getConnection(getUrl(), props); + Connection conn2 = sameClient ? conn1 : DriverManager.getConnection(getUrl(), props)) { + // create sequence for auto partition + conn1.createStatement().execute("CREATE SEQUENCE metric_id_seq CACHE 1"); + // create base table + conn1.createStatement().execute("CREATE TABLE metric_table (metricId INTEGER NOT NULL, metricVal DOUBLE, CONSTRAINT PK PRIMARY KEY(metricId))" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=1, AUTO_PARTITION_SEQ=metric_id_seq"); + // create view + String ddl = + "CREATE VIEW IF NOT EXISTS" + + " view1( hostName varchar NOT NULL," + + " CONSTRAINT HOSTNAME_PK PRIMARY KEY (hostName))" + + " AS SELECT * FROM metric_table" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=300000"; + conn1.createStatement().execute(ddl); + + conn1.createStatement().execute("UPSERT INTO view1(hostName, metricVal) VALUES('host1', 1.0)"); + conn1.commit(); + + // execute ddl adding a pk column and regular column + ddl = + "CREATE VIEW IF NOT EXISTS" + + " view1( hostName varchar NOT NULL, instanceName varchar, metricVal2 double" + + " CONSTRAINT HOSTNAME_PK PRIMARY KEY (hostName, instancename))" + + " AS SELECT * FROM metric_table" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=300000"; + conn2.createStatement().execute(ddl); + + conn2.createStatement().execute( + "UPSERT INTO view1(hostName, instanceName, metricVal, metricval2) VALUES('host2', 'instance2', 21.0, 22.0)"); + conn2.commit(); + + conn1.createStatement().execute("UPSERT INTO view1(hostName, metricVal) VALUES('host3', 3.0)"); + conn1.commit(); + + // verify data exists + ResultSet rs = conn2.createStatement().executeQuery("SELECT * from view1"); + + // verify the two columns were added correctly + PTable table = + conn2.unwrap(PhoenixConnection.class).getTable(new PTableKey(null, "VIEW1")); + List<PColumn> pkColumns = table.getPKColumns(); + assertEquals(3,table.getPKColumns().size()); + assertEquals("METRICID", pkColumns.get(0).getName().getString()); + assertEquals("HOSTNAME", pkColumns.get(1).getName().getString()); + assertEquals("INSTANCENAME", pkColumns.get(2).getName().getString()); + List<PColumn> columns = table.getColumns(); + assertEquals("METRICID", columns.get(0).getName().getString()); + assertEquals("METRICVAL", columns.get(1).getName().getString()); + assertEquals("HOSTNAME", columns.get(2).getName().getString()); + assertEquals("INSTANCENAME", columns.get(3).getName().getString()); + assertEquals("METRICVAL2", columns.get(4).getName().getString()); + + // verify the data + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(1.0, rs.getDouble(2), 1e-6); + assertEquals("host1", rs.getString(3)); + assertEquals(null, rs.getString(4)); + assertEquals(0.0, rs.getDouble(5), 1e-6); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(21.0, rs.getDouble(2), 1e-6); + assertEquals("host2", rs.getString(3)); + assertEquals("instance2", rs.getString(4)); + assertEquals(22.0, rs.getDouble(5), 1e-6); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(3.0, rs.getDouble(2), 1e-6); + assertEquals("host3", rs.getString(3)); + assertEquals(null, rs.getString(4)); + assertEquals(0.0, rs.getDouble(5), 1e-6); + assertFalse(rs.next()); + } + } + + @Test + public void testCreateTableAddColumnsSameClient() throws Exception { + createTableAddColumns(true); + } + + @Test + public void testCreateTableAddColumnsDifferentClient() throws Exception { + createTableAddColumns(false); + } + + public void testCreateTableDropColumns() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + String ddl = + "create table IF NOT EXISTS TEST( id1 char(2) NOT NULL," + " col1 integer," + + " col2 integer," + " CONSTRAINT NAME_PK PRIMARY KEY (id1))" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=300000"; + conn.createStatement().execute(ddl); + conn.createStatement().execute("UPSERT INTO TEST VALUES('a', 11)"); + conn.commit(); + + // execute ddl while dropping a column + ddl = "alter table TEST drop column col1"; + try { + conn.createStatement().execute(ddl); + fail("Dropping a column from a table with APPEND_ONLY_SCHEMA=true should fail"); + } catch (SQLException e) { + assertEquals(SQLExceptionCode.CANNOT_DROP_COL_APPEND_ONLY_SCHEMA.getErrorCode(), + e.getErrorCode()); + } + } + } + + @Test + public void testValidateAttributes() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + try { + conn.createStatement().execute( + "create table IF NOT EXISTS TEST1 ( id char(1) NOT NULL," + + " col1 integer NOT NULL," + + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1))" + + " APPEND_ONLY_SCHEMA = true"); + fail("UPDATE_CACHE_FREQUENCY attribute must not be set to ALWAYS if APPEND_ONLY_SCHEMA is true"); + } catch (SQLException e) { + assertEquals(SQLExceptionCode.UPDATE_CACHE_FREQUENCY_INVALID.getErrorCode(), + e.getErrorCode()); + } + + conn.createStatement().execute( + "create table IF NOT EXISTS TEST1 ( id char(1) NOT NULL," + + " col1 integer NOT NULL" + + " CONSTRAINT NAME_PK PRIMARY KEY (id, col1))" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=1000"); + try { + conn.createStatement().execute( + "create view IF NOT EXISTS MY_VIEW (val1 integer NOT NULL) AS SELECT * FROM TEST1" + + " UPDATE_CACHE_FREQUENCY=1000"); + fail("APPEND_ONLY_SCHEMA must be true for a view if it is true for the base table "); + } + catch (SQLException e) { + assertEquals(SQLExceptionCode.VIEW_APPEND_ONLY_SCHEMA.getErrorCode(), + e.getErrorCode()); + } + } + } + + @Test + public void testUpsertRowToDeletedTable() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn1 = DriverManager.getConnection(getUrl(), props); + Connection conn2 = DriverManager.getConnection(getUrl(), props)) { + // create sequence for auto partition + conn1.createStatement().execute("CREATE SEQUENCE metric_id_seq CACHE 1"); + // create base table + conn1.createStatement().execute("CREATE TABLE metric_table (metricId INTEGER NOT NULL, metricVal DOUBLE, CONSTRAINT PK PRIMARY KEY(metricId))" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=1, AUTO_PARTITION_SEQ=metric_id_seq"); + // create view + String ddl = + "CREATE VIEW IF NOT EXISTS" + + " view1( hostName varchar NOT NULL," + + " CONSTRAINT HOSTNAME_PK PRIMARY KEY (hostName))" + + " AS SELECT * FROM metric_table" + + " APPEND_ONLY_SCHEMA = true, UPDATE_CACHE_FREQUENCY=300000"; + conn1.createStatement().execute(ddl); + + // drop the table using a different connection + conn2.createStatement().execute("DROP VIEW view1"); + + // upsert one row + conn1.createStatement().execute("UPSERT INTO view1(hostName, metricVal) VALUES('host1', 1.0)"); + // upsert doesn't fail since base table still exists + conn1.commit(); + } + } + +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/it/java/org/apache/phoenix/end2end/AutoPartitionViewsIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AutoPartitionViewsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AutoPartitionViewsIT.java index 2b3f932..b21b772 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AutoPartitionViewsIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AutoPartitionViewsIT.java @@ -167,11 +167,11 @@ public class AutoPartitionViewsIT extends BaseHBaseManagedTimeIT { assertTrue("Partition column view referenced attribute should be true ", partitionCol3.isViewReferenced()); // verify viewConstant was set correctly - byte[] expectedPartition1 = new byte[Bytes.SIZEOF_LONG + 1]; + byte[] expectedPartition1 = new byte[Bytes.SIZEOF_INT + 1]; PInteger.INSTANCE.toBytes(Integer.MAX_VALUE - 2, expectedPartition1, 0); - byte[] expectedPartition2 = new byte[Bytes.SIZEOF_LONG + 1]; + byte[] expectedPartition2 = new byte[Bytes.SIZEOF_INT + 1]; PInteger.INSTANCE.toBytes(Integer.MAX_VALUE - 1, expectedPartition2, 0); - byte[] expectedPartition3 = new byte[Bytes.SIZEOF_LONG + 1]; + byte[] expectedPartition3 = new byte[Bytes.SIZEOF_INT + 1]; PInteger.INSTANCE.toBytes(Integer.MAX_VALUE, expectedPartition3, 0); assertArrayEquals("Unexpected Partition column view constant attribute", expectedPartition1, partitionCol1.getViewConstant()); http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java index 612adae..ca0a6c3 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java @@ -776,7 +776,7 @@ public class FromCompiler { MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM, null, null, columns, null, null, Collections.<PTable> emptyList(), false, Collections.<PName> emptyList(), null, null, false, false, false, null, null, null, false, false, 0, 0L, SchemaUtil - .isNamespaceMappingEnabled(PTableType.SUBQUERY, connection.getQueryServices().getProps()), null); + .isNamespaceMappingEnabled(PTableType.SUBQUERY, connection.getQueryServices().getProps()), null, false); String alias = subselectNode.getAlias(); TableRef tableRef = new TableRef(alias, t, MetaDataProtocol.MIN_TABLE_TIMESTAMP, false); http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java index 5317b49..4c18bf8 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java @@ -1307,7 +1307,8 @@ public class JoinCompiler { left.isImmutableRows(), Collections.<PName> emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL, left.isMultiTenant(), left.getStoreNulls(), left.getViewType(), left.getViewIndexId(), left.getIndexType(), left.rowKeyOrderOptimizable(), left.isTransactional(), - left.getUpdateCacheFrequency(), left.getIndexDisableTimestamp(), left.isNamespaceMapped(), left.getAutoPartitionSeqName()); + left.getUpdateCacheFrequency(), left.getIndexDisableTimestamp(), left.isNamespaceMapped(), + left.getAutoPartitionSeqName(), left.isAppendOnlySchema()); } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java index 8c3d399..4d3c0cf 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java @@ -153,7 +153,8 @@ public class TupleProjectionCompiler { table.getParentName(), table.getIndexes(), table.isImmutableRows(), Collections.<PName> emptyList(), null, null, table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), - table.getIndexType(), table.rowKeyOrderOptimizable(), table.isTransactional(), table.getUpdateCacheFrequency(), table.getIndexDisableTimestamp(), table.isNamespaceMapped(), table.getAutoPartitionSeqName()); + table.getIndexType(), table.rowKeyOrderOptimizable(), table.isTransactional(), table.getUpdateCacheFrequency(), + table.getIndexDisableTimestamp(), table.isNamespaceMapped(), table.getAutoPartitionSeqName(), table.isAppendOnlySchema()); } public static PTable createProjectedTable(TableRef tableRef, List<ColumnRef> sourceColumnRefs, boolean retainPKColumns) throws SQLException { @@ -181,7 +182,7 @@ public class TupleProjectionCompiler { Collections.<PTable> emptyList(), table.isImmutableRows(), Collections.<PName> emptyList(), null, null, table.isWALDisabled(), table.isMultiTenant(), table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), null, table.rowKeyOrderOptimizable(), table.isTransactional(), - table.getUpdateCacheFrequency(), table.getIndexDisableTimestamp(), table.isNamespaceMapped(), table.getAutoPartitionSeqName()); + table.getUpdateCacheFrequency(), table.getIndexDisableTimestamp(), table.isNamespaceMapped(), table.getAutoPartitionSeqName(), table.isAppendOnlySchema()); } // For extracting column references from single select statement http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java index 71cd7fc..8dd350c 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java @@ -85,7 +85,7 @@ public class UnionCompiler { scn == null ? HConstants.LATEST_TIMESTAMP : scn, null, null, projectedColumns, null, null, null, true, null, null, null, true, true, true, null, null, null, false, false, 0, 0L, SchemaUtil.isNamespaceMappingEnabled(PTableType.SUBQUERY, - statement.getConnection().getQueryServices().getProps()), null); + statement.getConnection().getQueryServices().getProps()), null, false); TableRef tableRef = new TableRef(null, tempTable, 0, false); return tableRef; } http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java index 25483a3..7f222bb 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java @@ -20,6 +20,7 @@ package org.apache.phoenix.coprocessor; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static org.apache.hadoop.hbase.KeyValueUtil.createFirstOnRow; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.APPEND_ONLY_SCHEMA_BYTES; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARRAY_SIZE_BYTES; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.AUTO_PARTITION_SEQ_BYTES; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.CLASS_NAME_BYTES; @@ -277,6 +278,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso private static final KeyValue IS_NAMESPACE_MAPPED_KV = createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, IS_NAMESPACE_MAPPED_BYTES); private static final KeyValue AUTO_PARTITION_SEQ_KV = createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, AUTO_PARTITION_SEQ_BYTES); + private static final KeyValue APPEND_ONLY_SCHEMA_KV = createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, APPEND_ONLY_SCHEMA_BYTES); private static final List<KeyValue> TABLE_KV_COLUMNS = Arrays.<KeyValue>asList( EMPTY_KEYVALUE_KV, @@ -302,7 +304,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso TRANSACTIONAL_KV, UPDATE_CACHE_FREQUENCY_KV, IS_NAMESPACE_MAPPED_KV, - AUTO_PARTITION_SEQ_KV + AUTO_PARTITION_SEQ_KV, + APPEND_ONLY_SCHEMA_KV ); static { Collections.sort(TABLE_KV_COLUMNS, KeyValue.COMPARATOR); @@ -331,6 +334,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso private static final int INDEX_DISABLE_TIMESTAMP = TABLE_KV_COLUMNS.indexOf(INDEX_DISABLE_TIMESTAMP_KV); private static final int IS_NAMESPACE_MAPPED_INDEX = TABLE_KV_COLUMNS.indexOf(IS_NAMESPACE_MAPPED_KV); private static final int AUTO_PARTITION_SEQ_INDEX = TABLE_KV_COLUMNS.indexOf(AUTO_PARTITION_SEQ_KV); + private static final int APPEND_ONLY_SCHEMA_INDEX = TABLE_KV_COLUMNS.indexOf(APPEND_ONLY_SCHEMA_KV); // KeyValues for Column private static final KeyValue DECIMAL_DIGITS_KV = createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, DECIMAL_DIGITS_BYTES); @@ -914,6 +918,11 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso Cell autoPartitionSeqKv = tableKeyValues[AUTO_PARTITION_SEQ_INDEX]; String autoPartitionSeq = autoPartitionSeqKv != null ? (String) PVarchar.INSTANCE.toObject(autoPartitionSeqKv.getValueArray(), autoPartitionSeqKv.getValueOffset(), autoPartitionSeqKv.getValueLength()) : null; + Cell isAppendOnlySchemaKv = tableKeyValues[APPEND_ONLY_SCHEMA_INDEX]; + boolean isAppendOnlySchema = isAppendOnlySchemaKv == null ? false + : Boolean.TRUE.equals(PBoolean.INSTANCE.toObject(isAppendOnlySchemaKv.getValueArray(), + isAppendOnlySchemaKv.getValueOffset(), isAppendOnlySchemaKv.getValueLength())); + List<PColumn> columns = Lists.newArrayListWithExpectedSize(columnCount); List<PTable> indexes = new ArrayList<PTable>(); @@ -964,7 +973,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso tableType == INDEX ? dataTableName : null, indexes, isImmutableRows, physicalTables, defaultFamilyName, viewStatement, disableWAL, multiTenant, storeNulls, viewType, viewIndexId, indexType, rowKeyOrderOptimizable, transactional, updateCacheFrequency, stats, baseColumnCount, - indexDisableTimestamp, isNamespaceMapped, autoPartitionSeq); + indexDisableTimestamp, isNamespaceMapped, autoPartitionSeq, isAppendOnlySchema); } private PSchema getSchema(RegionScanner scanner, long clientTimeStamp) throws IOException, SQLException { @@ -1453,7 +1462,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso done.run(builder.build()); return; } - PColumn autoPartitionCol = parentTable.getColumns().get(MetaDataUtil.getAutoPartitionColIndex(parentTable)); + PColumn autoPartitionCol = parentTable.getPKColumns().get(MetaDataUtil.getAutoPartitionColIndex(parentTable)); if (!PLong.INSTANCE.isCoercibleTo(autoPartitionCol.getDataType(), autoPartitionNum)) { builder.setReturnCode(MetaDataProtos.MutationCode.CANNOT_COERCE_AUTO_PARTITION_ID); builder.setMutationTime(EnvironmentEdgeManager.currentTimeMillis()); @@ -1490,9 +1499,9 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso familyCellMap = autoPartitionPut.getFamilyCellMap(); cells = familyCellMap.get(TABLE_FAMILY_BYTES); cell = cells.get(0); - byte[] bytes = new byte [Bytes.SIZEOF_LONG + 1]; PDataType dataType = autoPartitionCol.getDataType(); Object val = dataType.toObject(autoPartitionNum, PLong.INSTANCE); + byte[] bytes = new byte [dataType.getByteSize() + 1]; dataType.toBytes(val, bytes, 0); Cell viewConstantCell = new KeyValue(cell.getRow(), cell.getFamily(), VIEW_CONSTANT_BYTES, cell.getTimestamp(), Type.codeToType(cell.getTypeByte()), bytes); http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java index fca181d..4171680 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java @@ -3363,6 +3363,16 @@ public final class PTableProtos { */ com.google.protobuf.ByteString getAutoParititonSeqNameBytes(); + + // optional bool isAppendOnlySchema = 32; + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + boolean hasIsAppendOnlySchema(); + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + boolean getIsAppendOnlySchema(); } /** * Protobuf type {@code PTable} @@ -3588,6 +3598,11 @@ public final class PTableProtos { autoParititonSeqName_ = input.readBytes(); break; } + case 256: { + bitField0_ |= 0x08000000; + isAppendOnlySchema_ = input.readBool(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -4257,6 +4272,22 @@ public final class PTableProtos { } } + // optional bool isAppendOnlySchema = 32; + public static final int ISAPPENDONLYSCHEMA_FIELD_NUMBER = 32; + private boolean isAppendOnlySchema_; + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + public boolean hasIsAppendOnlySchema() { + return ((bitField0_ & 0x08000000) == 0x08000000); + } + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + public boolean getIsAppendOnlySchema() { + return isAppendOnlySchema_; + } + private void initFields() { schemaNameBytes_ = com.google.protobuf.ByteString.EMPTY; tableNameBytes_ = com.google.protobuf.ByteString.EMPTY; @@ -4289,6 +4320,7 @@ public final class PTableProtos { indexDisableTimestamp_ = 0L; isNamespaceMapped_ = false; autoParititonSeqName_ = ""; + isAppendOnlySchema_ = false; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -4449,6 +4481,9 @@ public final class PTableProtos { if (((bitField0_ & 0x04000000) == 0x04000000)) { output.writeBytes(31, getAutoParititonSeqNameBytes()); } + if (((bitField0_ & 0x08000000) == 0x08000000)) { + output.writeBool(32, isAppendOnlySchema_); + } getUnknownFields().writeTo(output); } @@ -4587,6 +4622,10 @@ public final class PTableProtos { size += com.google.protobuf.CodedOutputStream .computeBytesSize(31, getAutoParititonSeqNameBytes()); } + if (((bitField0_ & 0x08000000) == 0x08000000)) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(32, isAppendOnlySchema_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -4753,6 +4792,11 @@ public final class PTableProtos { result = result && getAutoParititonSeqName() .equals(other.getAutoParititonSeqName()); } + result = result && (hasIsAppendOnlySchema() == other.hasIsAppendOnlySchema()); + if (hasIsAppendOnlySchema()) { + result = result && (getIsAppendOnlySchema() + == other.getIsAppendOnlySchema()); + } result = result && getUnknownFields().equals(other.getUnknownFields()); return result; @@ -4890,6 +4934,10 @@ public final class PTableProtos { hash = (37 * hash) + AUTOPARITITONSEQNAME_FIELD_NUMBER; hash = (53 * hash) + getAutoParititonSeqName().hashCode(); } + if (hasIsAppendOnlySchema()) { + hash = (37 * hash) + ISAPPENDONLYSCHEMA_FIELD_NUMBER; + hash = (53 * hash) + hashBoolean(getIsAppendOnlySchema()); + } hash = (29 * hash) + getUnknownFields().hashCode(); memoizedHashCode = hash; return hash; @@ -5076,6 +5124,8 @@ public final class PTableProtos { bitField0_ = (bitField0_ & ~0x20000000); autoParititonSeqName_ = ""; bitField0_ = (bitField0_ & ~0x40000000); + isAppendOnlySchema_ = false; + bitField0_ = (bitField0_ & ~0x80000000); return this; } @@ -5244,6 +5294,10 @@ public final class PTableProtos { to_bitField0_ |= 0x04000000; } result.autoParititonSeqName_ = autoParititonSeqName_; + if (((from_bitField0_ & 0x80000000) == 0x80000000)) { + to_bitField0_ |= 0x08000000; + } + result.isAppendOnlySchema_ = isAppendOnlySchema_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -5433,6 +5487,9 @@ public final class PTableProtos { autoParititonSeqName_ = other.autoParititonSeqName_; onChanged(); } + if (other.hasIsAppendOnlySchema()) { + setIsAppendOnlySchema(other.getIsAppendOnlySchema()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -7309,6 +7366,39 @@ public final class PTableProtos { return this; } + // optional bool isAppendOnlySchema = 32; + private boolean isAppendOnlySchema_ ; + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + public boolean hasIsAppendOnlySchema() { + return ((bitField0_ & 0x80000000) == 0x80000000); + } + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + public boolean getIsAppendOnlySchema() { + return isAppendOnlySchema_; + } + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + public Builder setIsAppendOnlySchema(boolean value) { + bitField0_ |= 0x80000000; + isAppendOnlySchema_ = value; + onChanged(); + return this; + } + /** + * <code>optional bool isAppendOnlySchema = 32;</code> + */ + public Builder clearIsAppendOnlySchema() { + bitField0_ = (bitField0_ & ~0x80000000); + isAppendOnlySchema_ = false; + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:PTable) } @@ -7356,7 +7446,7 @@ public final class PTableProtos { "\016\n\006values\030\002 \003(\014\022\033\n\023guidePostsByteCount\030\003", " \001(\003\022\025\n\rkeyBytesCount\030\004 \001(\003\022\027\n\017guidePost" + "sCount\030\005 \001(\005\022!\n\013pGuidePosts\030\006 \001(\0132\014.PGui" + - "dePosts\"\374\005\n\006PTable\022\027\n\017schemaNameBytes\030\001 " + + "dePosts\"\230\006\n\006PTable\022\027\n\017schemaNameBytes\030\001 " + "\002(\014\022\026\n\016tableNameBytes\030\002 \002(\014\022\036\n\ttableType" + "\030\003 \002(\0162\013.PTableType\022\022\n\nindexState\030\004 \001(\t\022" + "\026\n\016sequenceNumber\030\005 \002(\003\022\021\n\ttimeStamp\030\006 \002" + @@ -7375,10 +7465,11 @@ public final class PTableProtos { "nsactional\030\033 \001(\010\022\034\n\024updateCacheFrequency" + "\030\034 \001(\003\022\035\n\025indexDisableTimestamp\030\035 \001(\003\022\031\n", "\021isNamespaceMapped\030\036 \001(\010\022\034\n\024autoParitito" + - "nSeqName\030\037 \001(\t*A\n\nPTableType\022\n\n\006SYSTEM\020\000" + - "\022\010\n\004USER\020\001\022\010\n\004VIEW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020" + - "\004B@\n(org.apache.phoenix.coprocessor.gene" + - "ratedB\014PTableProtosH\001\210\001\001\240\001\001" + "nSeqName\030\037 \001(\t\022\032\n\022isAppendOnlySchema\030 \001" + + "(\010*A\n\nPTableType\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010" + + "\n\004VIEW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004B@\n(org.apa" + + "che.phoenix.coprocessor.generatedB\014PTabl" + + "eProtosH\001\210\001\001\240\001\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -7402,7 +7493,7 @@ public final class PTableProtos { internal_static_PTable_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_PTable_descriptor, - new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", "TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", "BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", "DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", "ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", "IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", "RowKeyOrderOptimizable", "Transactional", "UpdateCacheFrequency", "IndexDisableTimestamp", "IsNamespaceMapped", "AutoParititonSeqName", }); + new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", "TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", "BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", "DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", "ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", "IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", "RowKeyOrderOptimizable", "Transactional", "UpdateCacheFrequency", "IndexDisableTimestamp", "IsNamespaceMapped", "AutoParititonSeqName", "IsAppendOnlySchema", }); return null; } }; http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java index 5661098..8064ce1 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java @@ -351,6 +351,10 @@ public enum SQLExceptionCode { CANNOT_SALT_LOCAL_INDEX(1110,"XCL10", "Local index may not be salted."), INDEX_FAILURE_BLOCK_WRITE(1120, "XCL20", "Writes to table blocked until index can be updated."), + + UPDATE_CACHE_FREQUENCY_INVALID(1130, "XCL30", "UPDATE_CACHE_FREQUENCY cannot be set to ALWAYS if APPEND_ONLY_SCHEMA is true."), + CANNOT_DROP_COL_APPEND_ONLY_SCHEMA(1131, "XCL31", "Cannot drop column from table that with append only schema."), + VIEW_APPEND_ONLY_SCHEMA(1132, "XCL32", "APPEND_ONLY_SCHEMA property of view must match the base table"), /** * Implementation defined class. Phoenix internal error. (errorcode 20, sqlstate INT). http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java index c571625..344fc3e 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java @@ -295,6 +295,9 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData { public static final String AUTO_PARTITION_SEQ = "AUTO_PARTITION_SEQ"; public static final byte[] AUTO_PARTITION_SEQ_BYTES = Bytes.toBytes(AUTO_PARTITION_SEQ); + public static final String APPEND_ONLY_SCHEMA = "APPEND_ONLY_SCHEMA"; + public static final byte[] APPEND_ONLY_SCHEMA_BYTES = Bytes.toBytes(APPEND_ONLY_SCHEMA); + public static final String ASYNC_CREATED_DATE = "ASYNC_CREATED_DATE"; private final PhoenixConnection connection; http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/parse/ColumnDef.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ColumnDef.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ColumnDef.java index 278b4aa..401d57b 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ColumnDef.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ColumnDef.java @@ -43,7 +43,7 @@ public class ColumnDef { private final Boolean isNull; private final Integer maxLength; private final Integer scale; - private final boolean isPK; + private boolean isPK; private final SortOrder sortOrder; private final boolean isArray; private final Integer arrSize; @@ -185,6 +185,11 @@ public class ColumnDef { public boolean isRowTimestamp() { return isRowTimestamp; } + + public void setIsPK(boolean isPK) { + this.isPK = isPK; + } + @Override public String toString() { StringBuilder buf = new StringBuilder(columnDefName.getColumnNode().toString()); http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/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 667d89c..931ecfc 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 @@ -195,6 +195,12 @@ import org.apache.twill.zookeeper.ZKClients; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import co.cask.tephra.TransactionSystemClient; +import co.cask.tephra.TxConstants; +import co.cask.tephra.distributed.PooledClientProvider; +import co.cask.tephra.distributed.TransactionServiceClient; +import co.cask.tephra.zookeeper.TephraZKClientService; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Throwables; @@ -208,12 +214,6 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import co.cask.tephra.TransactionSystemClient; -import co.cask.tephra.TxConstants; -import co.cask.tephra.distributed.PooledClientProvider; -import co.cask.tephra.distributed.TransactionServiceClient; -import co.cask.tephra.zookeeper.TephraZKClientService; - public class ConnectionQueryServicesImpl extends DelegateQueryServices implements ConnectionQueryServices { private static final Logger logger = LoggerFactory.getLogger(ConnectionQueryServicesImpl.class); private static final int INITIAL_CHILD_SERVICES_CAPACITY = 100; @@ -1937,13 +1937,13 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement for (Pair<String, Object> prop : propsList) { String propName = prop.getFirst(); Object propValue = prop.getSecond(); - if ((isHTableProperty(propName) || TableProperty.isPhoenixTableProperty(propName)) && addingColumns) { + if ((MetaDataUtil.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)) { + if (MetaDataUtil.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) @@ -1964,7 +1964,7 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement tableProps.put(TxConstants.READ_NON_TX_DATA, propValue); } } else { - if (isHColumnProperty(propName)) { + if (MetaDataUtil.isHColumnProperty(propName)) { if (family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) { commonFamilyProps.put(propName, propValue); } else { @@ -2170,14 +2170,6 @@ public class ConnectionQueryServicesImpl extends DelegateQueryServices implement } } - private boolean isHColumnProperty(String propName) { - return HColumnDescriptor.getDefaultValues().containsKey(propName); - } - - private boolean isHTableProperty(String propName) { - return !isHColumnProperty(propName) && !TableProperty.isPhoenixTableProperty(propName); - } - private HashSet<String> existingColumnFamiliesForBaseTable(PName baseTableName) throws TableNotFoundException { synchronized (latestMetaDataLock) { throwConnectionClosedIfNullMetaData(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java index 5c7ac1a..4efb708 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java @@ -18,6 +18,7 @@ package org.apache.phoenix.query; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.APPEND_ONLY_SCHEMA; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARG_POSITION; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARRAY_SIZE; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.AUTO_PARTITION_SEQ; @@ -269,6 +270,7 @@ public interface QueryConstants { UPDATE_CACHE_FREQUENCY + " BIGINT," + IS_NAMESPACE_MAPPED + " BOOLEAN," + AUTO_PARTITION_SEQ + " VARCHAR," + + APPEND_ONLY_SCHEMA + " BOOLEAN," + "CONSTRAINT " + SYSTEM_TABLE_PK_NAME + " PRIMARY KEY (" + TENANT_ID + "," + TABLE_SCHEM + "," + TABLE_NAME + "," + COLUMN_NAME + "," + COLUMN_FAMILY + "))\n" + HConstants.VERSIONS + "=" + MetaDataProtocol.DEFAULT_MAX_META_DATA_VERSIONS + ",\n" + http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java index f1a7548..ddd2de0 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateTable.java @@ -277,7 +277,13 @@ public class DelegateTable implements PTable { return delegate.isNamespaceMapped(); } + @Override public String getAutoPartitionSeqName() { return delegate.getAutoPartitionSeqName(); } + + @Override + public boolean isAppendOnlySchema() { + return delegate.isAppendOnlySchema(); + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/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 95fde4b..2658174 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 @@ -24,6 +24,7 @@ import static org.apache.hadoop.hbase.HColumnDescriptor.TTL; import static org.apache.phoenix.coprocessor.BaseScannerRegionObserver.ANALYZE_TABLE; import static org.apache.phoenix.coprocessor.BaseScannerRegionObserver.RUN_UPDATE_STATS_ASYNC_ATTRIB; import static org.apache.phoenix.exception.SQLExceptionCode.INSUFFICIENT_MULTI_TENANT_COLUMNS; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.APPEND_ONLY_SCHEMA; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARG_POSITION; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARRAY_SIZE; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ASYNC_CREATED_DATE; @@ -259,8 +260,9 @@ public class MetaDataClient { TRANSACTIONAL + "," + UPDATE_CACHE_FREQUENCY + "," + IS_NAMESPACE_MAPPED + "," + - AUTO_PARTITION_SEQ + - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + AUTO_PARTITION_SEQ + "," + + APPEND_ONLY_SCHEMA + + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; private static final String CREATE_SCHEMA = "UPSERT INTO " + SYSTEM_CATALOG_SCHEMA + ".\"" + SYSTEM_CATALOG_TABLE + "\"( " + TABLE_SCHEM + "," + TABLE_NAME + ") VALUES (?,?)"; @@ -863,7 +865,87 @@ public class MetaDataClient { } public MutationState createTable(CreateTableStatement statement, byte[][] splits, PTable parent, String viewStatement, ViewType viewType, byte[][] viewColumnConstants, BitSet isViewColumnReferenced) throws SQLException { - PTable table = createTableInternal(statement, splits, parent, viewStatement, viewType, viewColumnConstants, isViewColumnReferenced, null, null, null); + TableName tableName = statement.getTableName(); + Map<String,Object> tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); + Map<String,Object> commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size() + 1); + populatePropertyMaps(statement.getProps(), tableProps, commonFamilyProps); + + boolean isAppendOnlySchema = false; + Boolean appendOnlySchemaProp = (Boolean) TableProperty.APPEND_ONLY_SCHEMA.getValue(tableProps); + if (appendOnlySchemaProp != null) { + isAppendOnlySchema = appendOnlySchemaProp; + } + long updateCacheFrequency = 0; + Long updateCacheFrequencyProp = (Long) TableProperty.UPDATE_CACHE_FREQUENCY.getValue(tableProps); + if (updateCacheFrequencyProp != null) { + updateCacheFrequency = updateCacheFrequencyProp; + } + // updateCacheFrequency cannot be set to ALWAYS if isAppendOnlySchema is true + if (isAppendOnlySchema && updateCacheFrequency==0) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.UPDATE_CACHE_FREQUENCY_INVALID) + .setSchemaName(tableName.getSchemaName()).setTableName(tableName.getTableName()) + .build().buildException(); + } + // view isAppendOnlySchema property must match the parent table + if (parent!=null && isAppendOnlySchema!= parent.isAppendOnlySchema()) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_APPEND_ONLY_SCHEMA) + .setSchemaName(tableName.getSchemaName()).setTableName(tableName.getTableName()) + .build().buildException(); + } + + PTable table = null; + // if the APPEND_ONLY_SCHEMA attribute is true first check if the table is present in the cache + // if it is add columns that are not already present + if (isAppendOnlySchema) { + // look up the table in the cache + MetaDataMutationResult result = updateCache(tableName.getSchemaName(), tableName.getTableName()); + if (result.getMutationCode()==MutationCode.TABLE_ALREADY_EXISTS) { + table = result.getTable(); + if (!statement.ifNotExists()) { + throw new NewerTableAlreadyExistsException(tableName.getSchemaName(), tableName.getTableName(), table); + } + + List<ColumnDef> columnDefs = statement.getColumnDefs(); + PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint(); + // get the list of columns to add + List<ColumnDef> newColumnDefs = Lists.newArrayList(); + for (ColumnDef columnDef : columnDefs) { + if (pkConstraint.contains(columnDef.getColumnDefName())) { + columnDef.setIsPK(true); + } + String familyName = columnDef.getColumnDefName().getFamilyName(); + String columnName = columnDef.getColumnDefName().getColumnName(); + if (familyName!=null) { + try { + PColumnFamily columnFamily = table.getColumnFamily(familyName); + columnFamily.getColumn(columnName); + } + catch (ColumnFamilyNotFoundException | ColumnNotFoundException e){ + newColumnDefs.add(columnDef); + } + } + else { + try { + table.getColumn(columnName); + } + catch (ColumnNotFoundException e){ + newColumnDefs.add(columnDef); + } + } + } + // if there are new columns to add + if (!newColumnDefs.isEmpty()) { + return addColumn(table, newColumnDefs, statement.getProps(), + statement.ifNotExists(), true, + NamedTableNode.create(statement.getTableName()), statement.getTableType()); + } + else { + return new MutationState(0,connection); + } + } + } + table = createTableInternal(statement, splits, parent, viewStatement, viewType, viewColumnConstants, isViewColumnReferenced, null, null, null, tableProps, commonFamilyProps); + if (table == null || table.getType() == PTableType.VIEW || table.isTransactional()) { return new MutationState(0,connection); } @@ -883,6 +965,22 @@ public class MetaDataClient { return connection.getQueryServices().updateData(plan); } + private void populatePropertyMaps(ListMultimap<String,Pair<String,Object>> props, Map<String, Object> tableProps, + Map<String, Object> commonFamilyProps) { + // Somewhat hacky way of determining if property is for HColumnDescriptor or HTableDescriptor + HColumnDescriptor defaultDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); + if (!props.isEmpty()) { + Collection<Pair<String,Object>> propsList = props.get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY); + for (Pair<String,Object> prop : propsList) { + if (defaultDescriptor.getValue(prop.getFirst()) == null) { + tableProps.put(prop.getFirst(), prop.getSecond()); + } else { + commonFamilyProps.put(prop.getFirst(), prop.getSecond()); + } + } + } + } + public MutationState updateStatistics(UpdateStatisticsStatement updateStatisticsStmt) throws SQLException { // Don't mistakenly commit pending rows @@ -1160,11 +1258,14 @@ public class MetaDataClient { IndexKeyConstraint ik = statement.getIndexConstraint(); TableName indexTableName = statement.getIndexTableName(); + Map<String,Object> tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); + Map<String,Object> commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size() + 1); + populatePropertyMaps(statement.getProps(), tableProps, commonFamilyProps); List<Pair<ParseNode, SortOrder>> indexParseNodeAndSortOrderList = ik.getParseNodeAndSortOrderList(); List<ColumnName> includedColumns = statement.getIncludeColumns(); TableRef tableRef = null; PTable table = null; - boolean retry = true; + int numRetries = 0; Short indexId = null; boolean allocateIndexId = false; boolean isLocalIndex = statement.getIndexType() == IndexType.LOCAL; @@ -1359,11 +1460,11 @@ public class MetaDataClient { } PrimaryKeyConstraint pk = FACTORY.primaryKey(null, allPkColumns); CreateTableStatement tableStatement = FACTORY.createTable(indexTableName, statement.getProps(), columnDefs, pk, statement.getSplitNodes(), PTableType.INDEX, statement.ifNotExists(), null, null, statement.getBindCount()); - table = createTableInternal(tableStatement, splits, dataTable, null, null, null, null, indexId, statement.getIndexType(), asyncCreatedDate); + table = createTableInternal(tableStatement, splits, dataTable, null, null, null, null, indexId, statement.getIndexType(), asyncCreatedDate, tableProps, commonFamilyProps); break; } catch (ConcurrentTableMutationException e) { // Can happen if parent data table changes while above is in progress - if (retry) { - retry = false; + if (numRetries<5) { + numRetries++; continue; } throw e; @@ -1541,7 +1642,12 @@ public class MetaDataClient { return false; } - private PTable createTableInternal(CreateTableStatement statement, byte[][] splits, final PTable parent, String viewStatement, ViewType viewType, final byte[][] viewColumnConstants, final BitSet isViewColumnReferenced, Short indexId, IndexType indexType, Date asyncCreatedDate) throws SQLException { + private PTable createTableInternal(CreateTableStatement statement, byte[][] splits, + final PTable parent, String viewStatement, ViewType viewType, + final byte[][] viewColumnConstants, final BitSet isViewColumnReferenced, Short indexId, + IndexType indexType, Date asyncCreatedDate, + Map<String,Object> tableProps, + Map<String,Object> commonFamilyProps) throws SQLException { final PTableType tableType = statement.getTableType(); boolean wasAutoCommit = connection.getAutoCommit(); connection.rollback(); @@ -1563,6 +1669,7 @@ public class MetaDataClient { Integer saltBucketNum = null; String defaultFamilyName = null; boolean isImmutableRows = false; + boolean isAppendOnlySchema = false; List<PName> physicalNames = Collections.emptyList(); boolean addSaltColumn = false; boolean rowKeyOrderOptimizable = true; @@ -1574,6 +1681,7 @@ public class MetaDataClient { timestamp = TransactionUtil.getTableTimestamp(connection, transactional); storeNulls = parent.getStoreNulls(); isImmutableRows = parent.isImmutableRows(); + isAppendOnlySchema = parent.isAppendOnlySchema(); // Index on view // TODO: Can we support a multi-tenant index directly on a multi-tenant // table instead of only a view? We don't have anywhere to put the link @@ -1630,21 +1738,6 @@ public class MetaDataClient { pkName = pkConstraint.getName(); } - Map<String,Object> tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); - Map<String,Object> commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size() + 1); - // Somewhat hacky way of determining if property is for HColumnDescriptor or HTableDescriptor - HColumnDescriptor defaultDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); - if (!statement.getProps().isEmpty()) { - Collection<Pair<String,Object>> props = statement.getProps().get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY); - for (Pair<String,Object> prop : props) { - if (defaultDescriptor.getValue(prop.getFirst()) == null) { - tableProps.put(prop.getFirst(), prop.getSecond()); - } else { - commonFamilyProps.put(prop.getFirst(), prop.getSecond()); - } - } - } - // Although unusual, it's possible to set a mapped VIEW as having immutable rows. // This tells Phoenix that you're managing the index maintenance yourself. if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW || viewType == ViewType.MAPPED)) { @@ -1655,6 +1748,11 @@ public class MetaDataClient { isImmutableRows = isImmutableRowsProp; } } + + if (tableType == PTableType.TABLE) { + Boolean isAppendOnlySchemaProp = (Boolean) TableProperty.APPEND_ONLY_SCHEMA.getValue(tableProps); + isAppendOnlySchema = isAppendOnlySchemaProp!=null ? isAppendOnlySchemaProp : false; + } // Can't set any of these on views or shared indexes on views if (tableType != PTableType.VIEW && indexId == null) { @@ -1831,6 +1929,7 @@ public class MetaDataClient { } multiTenant = parent.isMultiTenant(); saltBucketNum = parent.getBucketNum(); + isAppendOnlySchema = parent.isAppendOnlySchema(); isImmutableRows = parent.isImmutableRows(); disableWAL = (disableWALProp == null ? parent.isWALDisabled() : disableWALProp); defaultFamilyName = parent.getDefaultFamilyName() == null ? null : parent.getDefaultFamilyName().getString(); @@ -2036,7 +2135,7 @@ public class MetaDataClient { Collections.<PTable>emptyList(), isImmutableRows, Collections.<PName>emptyList(), defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), null, - Boolean.TRUE.equals(disableWAL), false, false, null, indexId, indexType, true, false, 0, 0L, isNamespaceMapped, autoPartitionSeq); + Boolean.TRUE.equals(disableWAL), false, false, null, indexId, indexType, true, false, 0, 0L, isNamespaceMapped, autoPartitionSeq, isAppendOnlySchema); connection.addTable(table, MetaDataProtocol.MIN_TABLE_TIMESTAMP); } else if (tableType == PTableType.INDEX && indexId == null) { if (tableProps.get(HTableDescriptor.MAX_FILESIZE) == null) { @@ -2072,7 +2171,7 @@ public class MetaDataClient { // For client-side cache, we need to update the column // set the autoPartition column attributes if (parent != null && parent.getAutoPartitionSeqName() != null - && MetaDataUtil.getAutoPartitionColIndex(parent) == columnPosition) { + && parent.getPKColumns().get(MetaDataUtil.getAutoPartitionColIndex(parent)).equals(column)) { columns.set(i, column = new DelegateColumn(column) { @Override public byte[] getViewConstant() { @@ -2176,6 +2275,7 @@ public class MetaDataClient { } else { tableUpsert.setString(24, autoPartitionSeq); } + tableUpsert.setBoolean(25, isAppendOnlySchema); tableUpsert.execute(); if (asyncCreatedDate != null) { @@ -2243,15 +2343,21 @@ public class MetaDataClient { // If the parent table of the view has the auto partition sequence name attribute, // set the view statement and relevant partition column attributes correctly if (parent!=null && parent.getAutoPartitionSeqName()!=null) { - int autoPartitionColIndex = parent.isMultiTenant() ? 1 : 0; + int autoPartitionColIndex = -1; + PColumn autoPartitionCol = parent.getPKColumns().get(MetaDataUtil.getAutoPartitionColIndex(parent)); + for (int i=0; i<columns.size(); ++i) { + if (autoPartitionCol.getName().equals(columns.get(i).getName())) { + autoPartitionColIndex = i; + } + } final Long autoPartitionNum = Long.valueOf(result.getAutoPartitionNum()); final PColumn column = columns.get(autoPartitionColIndex); columns.set(autoPartitionColIndex, new DelegateColumn(column) { @Override public byte[] getViewConstant() { - byte[] bytes = new byte [Bytes.SIZEOF_LONG + 1]; PDataType dataType = column.getDataType(); Object val = dataType.toObject(autoPartitionNum, PLong.INSTANCE); + byte[] bytes = new byte [dataType.getByteSize() + 1]; dataType.toBytes(val, bytes, 0); return bytes; } @@ -2274,7 +2380,7 @@ public class MetaDataClient { PTable.INITIAL_SEQ_NUM, pkName == null ? null : PNameFactory.newName(pkName), saltBucketNum, columns, dataTableName == null ? null : newSchemaName, dataTableName == null ? null : PNameFactory.newName(dataTableName), Collections.<PTable>emptyList(), isImmutableRows, physicalNames, defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), viewStatement, Boolean.TRUE.equals(disableWAL), multiTenant, storeNulls, viewType, - indexId, indexType, rowKeyOrderOptimizable, transactional, updateCacheFrequency, 0L, isNamespaceMapped, autoPartitionSeq); + indexId, indexType, rowKeyOrderOptimizable, transactional, updateCacheFrequency, 0L, isNamespaceMapped, autoPartitionSeq, isAppendOnlySchema); result = new MetaDataMutationResult(code, result.getMutationTime(), table, true); addTableToCache(result); return table; @@ -2682,16 +2788,23 @@ public class MetaDataClient { tableBoolUpsert.execute(); } } - + public MutationState addColumn(AddColumnStatement statement) throws SQLException { + PTable table = FromCompiler.getResolver(statement, connection).getTables().get(0).getTable(); + return addColumn(table, statement.getColumnDefs(), statement.getProps(), statement.ifNotExists(), false, statement.getTable(), statement.getTableType()); + } + + public MutationState addColumn(PTable table, List<ColumnDef> columnDefs, + ListMultimap<String, Pair<String, Object>> stmtProperties, boolean ifNotExists, + boolean removeTableProps, NamedTableNode namedTableNode, PTableType tableType) + throws SQLException { connection.rollback(); boolean wasAutoCommit = connection.getAutoCommit(); try { connection.setAutoCommit(false); PName tenantId = connection.getTenantId(); - TableName tableNameNode = statement.getTable().getName(); - String schemaName = tableNameNode.getSchemaName(); - String tableName = tableNameNode.getTableName(); + String schemaName = table.getSchemaName().getString(); + String tableName = table.getTableName().getString(); Boolean isImmutableRowsProp = null; Boolean multiTenantProp = null; @@ -2700,17 +2813,14 @@ public class MetaDataClient { Boolean isTransactionalProp = null; Long updateCacheFrequencyProp = null; - ListMultimap<String,Pair<String,Object>> stmtProperties = statement.getProps(); Map<String, List<Pair<String, Object>>> properties = new HashMap<>(stmtProperties.size()); - TableRef tableRef = FromCompiler.getResolver(statement, connection).getTables().get(0); - PTable table = tableRef.getTable(); - List<ColumnDef> columnDefs = statement.getColumnDefs(); if (columnDefs == null) { columnDefs = Collections.emptyList(); } for (String family : stmtProperties.keySet()) { - List<Pair<String, Object>> propsList = stmtProperties.get(family); - for (Pair<String, Object> prop : propsList) { + List<Pair<String, Object>> origPropsList = stmtProperties.get(family); + List<Pair<String, Object>> propsList = Lists.newArrayListWithExpectedSize(origPropsList.size()); + for (Pair<String, Object> prop : origPropsList) { String propName = prop.getFirst(); if (TableProperty.isPhoenixTableProperty(propName)) { TableProperty tableProp = TableProperty.valueOf(propName); @@ -2729,7 +2839,11 @@ public class MetaDataClient { } else if (propName.equals(UPDATE_CACHE_FREQUENCY)) { updateCacheFrequencyProp = (Long)value; } - } + } + // if removeTableProps is true only add the property if it is not a HTable or Phoenix Table property + if (!removeTableProps || (!TableProperty.isPhoenixTableProperty(propName) && !MetaDataUtil.isHTableProperty(propName))) { + propsList.add(prop); + } } properties.put(family, propsList); } @@ -2737,7 +2851,7 @@ public class MetaDataClient { boolean changingPhoenixTableProperty = false; boolean nonTxToTx = false; while (true) { - ColumnResolver resolver = FromCompiler.getResolver(statement, connection); + ColumnResolver resolver = FromCompiler.getResolver(namedTableNode, connection); table = resolver.getTables().get(0).getTable(); int nIndexes = table.getIndexes().size(); int nNewColumns = columnDefs.size(); @@ -2929,7 +3043,7 @@ public class MetaDataClient { } long seqNum = table.getSequenceNumber(); if (changingPhoenixTableProperty || columnDefs.size() > 0) { - seqNum = incrementTableSeqNum(table, statement.getTableType(), columnDefs.size(), isTransactional, updateCacheFrequency, isImmutableRows, disableWAL, multiTenant, storeNulls); + seqNum = incrementTableSeqNum(table, tableType, columnDefs.size(), isTransactional, updateCacheFrequency, isImmutableRows, disableWAL, multiTenant, storeNulls); tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond()); connection.rollback(); } @@ -2962,7 +3076,7 @@ public class MetaDataClient { MutationCode code = processMutationResult(schemaName, tableName, result); if (code == MutationCode.COLUMN_ALREADY_EXISTS) { addTableToCache(result); - if (!statement.ifNotExists()) { + if (!ifNotExists) { throw new ColumnAlreadyExistsException(schemaName, tableName, SchemaUtil.findExistingColumn(result.getTable(), columns)); } return new MutationState(0,connection); @@ -3124,6 +3238,7 @@ public class MetaDataClient { final ColumnResolver resolver = FromCompiler.getResolver(statement, connection); TableRef tableRef = resolver.getTables().get(0); PTable table = tableRef.getTable(); + List<ColumnName> columnRefs = statement.getColumnRefs(); if(columnRefs == null) { columnRefs = Lists.newArrayListWithCapacity(0); @@ -3149,6 +3264,10 @@ public class MetaDataClient { throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_DROP_PK) .setColumnName(columnToDrop.getName().getString()).build().buildException(); } + else if (table.isAppendOnlySchema()) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_DROP_COL_APPEND_ONLY_SCHEMA) + .setColumnName(columnToDrop.getName().getString()).build().buildException(); + } columnsToDrop.add(new ColumnRef(columnRef.getTableRef(), columnToDrop.getPosition())); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/a0504fba/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java index 5a3f18e..ca48fcb 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java @@ -349,9 +349,14 @@ public interface PTable extends PMetaDataEntity { boolean isNamespaceMapped(); /** - * * @return The sequence name used to get the unique identifier for views * that are automatically partitioned. */ - String getAutoPartitionSeqName(); + String getAutoPartitionSeqName(); + + /** + * @return true if the you can only add (and never delete) columns to the table, + * you are also not allowed to delete the table + */ + boolean isAppendOnlySchema(); }