This is an automated email from the ASF dual-hosted git repository. pboado pushed a commit to branch 4.x-cdh5.16 in repository https://gitbox.apache.org/repos/asf/phoenix.git
commit 4de622ab30d3f5aeb755ffaf786ec9ec4cdd3ba1 Author: s.kadam <s.ka...@salesforce.com> AuthorDate: Mon Dec 10 22:40:17 2018 +0000 PHOENIX-4983: Allow using a connection with a SCN set to write data to tables EXCEPT transactional tables or mutable tables with indexes or tables with ROW_TIMESTAMP column. --- .../apache/phoenix/end2end/UpsertWithSCNIT.java | 139 +++++++++++++++++++++ .../org/apache/phoenix/compile/UpsertCompiler.java | 23 +++- .../apache/phoenix/exception/SQLExceptionCode.java | 13 +- .../org/apache/phoenix/jdbc/PhoenixConnection.java | 2 +- 4 files changed, 172 insertions(+), 5 deletions(-) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertWithSCNIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertWithSCNIT.java new file mode 100644 index 0000000..6f231ff --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertWithSCNIT.java @@ -0,0 +1,139 @@ +package org.apache.phoenix.end2end; + +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Properties; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + + +public class UpsertWithSCNIT extends ParallelStatsDisabledIT { + + @Rule + public final ExpectedException exception = ExpectedException.none(); + Properties props = null; + PreparedStatement prep = null; + String tableName =null; + + private void helpTestUpserWithSCNIT(boolean rowColumn, boolean txTable, + boolean mutable, boolean local, boolean global) + throws SQLException { + + tableName = generateUniqueName(); + String indx; + String createTable = "CREATE TABLE "+tableName+" (" + + (rowColumn ? "CREATED_DATE DATE NOT NULL, ":"") + + "METRIC_ID CHAR(15) NOT NULL,METRIC_VALUE VARCHAR(50) CONSTRAINT PK PRIMARY KEY(" + + (rowColumn? "CREATED_DATE ROW_TIMESTAMP, ":"") + "METRIC_ID)) " + + (mutable? "IMMUTABLE_ROWS=false":"" ) + + (txTable ? "TRANSACTION_PROVIDER='TEPHRA',TRANSACTIONAL=true":""); + props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.createStatement().execute(createTable); + + if(local || global ){ + indx = "CREATE "+ (local? "LOCAL " : "") + "INDEX "+tableName+"_idx ON " + + ""+tableName+" (METRIC_VALUE)"; + conn.createStatement().execute(indx); + } + + props.setProperty("CurrentSCN", Long.toString(System.currentTimeMillis())); + conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(true); + String upsert = "UPSERT INTO "+tableName+" (METRIC_ID, METRIC_VALUE) VALUES (?,?)"; + prep = conn.prepareStatement(upsert); + prep.setString(1,"abc"); + prep.setString(2,"This is the first comment!"); + } + + @Test // See https://issues.apache.org/jira/browse/PHOENIX-4983 + public void testUpsertOnSCNSetTxnTable() throws SQLException { + + helpTestUpserWithSCNIT(false, true, false, false, false); + exception.expect(SQLException.class); + exception.expectMessage(containsString(String.valueOf( + SQLExceptionCode + .CANNOT_SPECIFY_SCN_FOR_TXN_TABLE + .getErrorCode()))); + prep.executeUpdate(); + } + + @Test + public void testUpsertOnSCNSetMutTableWithoutIdx() throws Exception { + + helpTestUpserWithSCNIT(false, false, true, false, false); + prep.executeUpdate(); + props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(),props); + ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM "+tableName); + assertTrue(rs.next()); + assertEquals("abc", rs.getString(1)); + assertEquals("This is the first comment!", rs.getString(2)); + assertFalse(rs.next()); + } + + @Test + public void testUpsertOnSCNSetTable() throws Exception { + + helpTestUpserWithSCNIT(false, false, false, false, false); + prep.executeUpdate(); + props = new Properties(); + Connection conn = DriverManager.getConnection(getUrl(),props); + ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM "+tableName); + assertTrue(rs.next()); + assertEquals("abc", rs.getString(1)); + assertEquals("This is the first comment!", rs.getString(2)); + assertFalse(rs.next()); + } + + @Test + public void testUpsertOnSCNSetMutTableWithLocalIdx() throws Exception { + + helpTestUpserWithSCNIT(false, false, true, true, false); + exception.expect(SQLException.class); + exception.expectMessage(containsString(String.valueOf( + SQLExceptionCode + .CANNOT_UPSERT_WITH_SCN_FOR_MUTABLE_TABLE_WITH_INDEXES + .getErrorCode()))); + prep.executeUpdate(); + } + @Test + public void testUpsertOnSCNSetMutTableWithGlobalIdx() throws Exception { + + helpTestUpserWithSCNIT(false, false, true, false, true); + exception.expect(SQLException.class); + exception.expectMessage(containsString(String.valueOf( + SQLExceptionCode + .CANNOT_UPSERT_WITH_SCN_FOR_MUTABLE_TABLE_WITH_INDEXES + .getErrorCode()))); + prep.executeUpdate(); + + } + @Test + public void testUpsertOnSCNSetWithRowTSColumn() throws Exception { + + helpTestUpserWithSCNIT(true, false, false, false, false); + exception.expect(SQLException.class); + exception.expectMessage(containsString(String.valueOf( + SQLExceptionCode + .CANNOT_UPSERT_WITH_SCN_FOR_ROW_TIMSTAMP_COLUMN + .getErrorCode()))); + prep.executeUpdate(); + } +} + diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java index a770339..ec0c67c 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java @@ -366,6 +366,8 @@ public class UpsertCompiler { // Cannot update: // - read-only VIEW // - transactional table with a connection having an SCN + // - mutable table with indexes and SCN set + // - tables with ROW_TIMESTAMP columns if (table.getType() == PTableType.VIEW && table.getViewType().isReadOnly()) { throw new ReadOnlyTableException(schemaName,tableName); } else if (connection.isBuildingIndex() && table.getType() != PTableType.INDEX) { @@ -374,8 +376,25 @@ public class UpsertCompiler { .setTableName(tableName) .build().buildException(); } else if (table.isTransactional() && connection.getSCN() != null) { - throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SPECIFY_SCN_FOR_TXN_TABLE).setSchemaName(schemaName) - .setTableName(tableName).build().buildException(); + throw new SQLExceptionInfo.Builder(SQLExceptionCode + .CANNOT_SPECIFY_SCN_FOR_TXN_TABLE) + .setSchemaName(schemaName) + .setTableName(tableName).build().buildException(); + } else if (!table.isImmutableRows() && connection.getSCN() != null + && !table.getIndexes().isEmpty() && !connection.isRunningUpgrade() + && !connection.isBuildingIndex()) { + throw new SQLExceptionInfo + .Builder(SQLExceptionCode + .CANNOT_UPSERT_WITH_SCN_FOR_MUTABLE_TABLE_WITH_INDEXES) + .setSchemaName(schemaName) + .setTableName(tableName).build().buildException(); + } else if(connection.getSCN() != null && !connection.isRunningUpgrade() + && !connection.isBuildingIndex() && table.getRowTimestampColPos() >= 0) { + throw new SQLExceptionInfo + .Builder(SQLExceptionCode + .CANNOT_UPSERT_WITH_SCN_FOR_ROW_TIMSTAMP_COLUMN) + .setSchemaName(schemaName) + .setTableName(tableName).build().buildException(); } boolean isSalted = table.getBucketNum() != null; isTenantSpecific = table.isMultiTenant() && connection.getTenantId() != null; 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 5bffed5..cf0b9ae 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 @@ -289,7 +289,8 @@ public enum SQLExceptionCode { TRANSACTION_FAILED(1077, "44A08", "Transaction Failure "), CANNOT_CREATE_TXN_TABLE_IF_TXNS_DISABLED(1078, "44A09", "Cannot create a transactional table if transactions are disabled."), CANNOT_ALTER_TO_BE_TXN_IF_TXNS_DISABLED(1079, "44A10", "Cannot alter table to be transactional table if transactions are disabled."), - CANNOT_CREATE_TXN_TABLE_WITH_ROW_TIMESTAMP(1080, "44A11", "Cannot create a transactional table if transactions are disabled."), + CANNOT_CREATE_TXN_TABLE_WITH_ROW_TIMESTAMP(1080, "44A11", "Cannot create a transactional" + + " table with ROW_TIMESTAMP column."), CANNOT_ALTER_TO_BE_TXN_WITH_ROW_TIMESTAMP(1081, "44A12", "Cannot alter table to be transactional table if transactions are disabled."), TX_MUST_BE_ENABLED_TO_SET_TX_CONTEXT(1082, "44A13", "Cannot set transaction context if transactions are disabled."), TX_MUST_BE_ENABLED_TO_SET_AUTO_FLUSH(1083, "44A14", "Cannot set auto flush if transactions are disabled."), @@ -461,7 +462,15 @@ public enum SQLExceptionCode { MAX_MUTATION_SIZE_EXCEEDED(729, "LIM01", "MutationState size is bigger than maximum allowed number of rows"), MAX_MUTATION_SIZE_BYTES_EXCEEDED(730, "LIM02", "MutationState size is bigger than maximum allowed number of bytes"), INSUFFICIENT_MEMORY(999, "50M01", "Unable to allocate enough memory."), - HASH_JOIN_CACHE_NOT_FOUND(900, "HJ01", "Hash Join cache not found"); + HASH_JOIN_CACHE_NOT_FOUND(900, "HJ01", "Hash Join cache not found"), + + CANNOT_UPSERT_WITH_SCN_FOR_ROW_TIMSTAMP_COLUMN(901,"43M12", + "Cannot use a connection with SCN set to upsert data for " + + "table with ROW_TIMESTAMP column."), + CANNOT_UPSERT_WITH_SCN_FOR_MUTABLE_TABLE_WITH_INDEXES(903,"43M14", + "Cannot use a connection with SCN set to " + + "upsert data for a mutable table with indexes."); + private final int errorCode; private final String sqlState; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java index 6da579f..596e27c 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java @@ -858,7 +858,7 @@ public class PhoenixConnection implements Connection, MetaDataMutated, SQLClosea @Override public boolean isReadOnly() throws SQLException { - return readOnly || (scn != null && !buildingIndex && !isRunningUpgrade); + return readOnly; } @Override