| /* |
| * 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.analyzeTable; |
| import static org.apache.phoenix.util.TestUtil.getAllSplits; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.sql.Connection; |
| import java.sql.DriverManager; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.hadoop.hbase.HConstants; |
| import org.apache.hadoop.hbase.client.Result; |
| import org.apache.hadoop.hbase.client.ResultScanner; |
| import org.apache.hadoop.hbase.client.Scan; |
| import org.apache.hadoop.hbase.client.Table; |
| import org.apache.hadoop.hbase.util.Bytes; |
| import org.apache.hadoop.hbase.util.Pair; |
| import org.apache.phoenix.exception.SQLExceptionCode; |
| import org.apache.phoenix.jdbc.PhoenixConnection; |
| import org.apache.phoenix.query.KeyRange; |
| import org.apache.phoenix.query.QueryServices; |
| import org.apache.phoenix.schema.ReadOnlyTableException; |
| import org.apache.phoenix.transaction.PhoenixTransactionProvider.Feature; |
| import org.apache.phoenix.transaction.TransactionFactory; |
| import org.apache.phoenix.util.ReadOnlyProps; |
| import org.apache.phoenix.util.ScanUtil; |
| import org.apache.phoenix.util.SchemaUtil; |
| import org.apache.phoenix.util.TestUtil; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| import org.junit.runners.Parameterized.Parameters; |
| |
| import com.google.common.collect.Maps; |
| |
| @RunWith(Parameterized.class) |
| public class StatsEnabledSplitSystemCatalogIT extends BaseUniqueNamesOwnClusterIT { |
| |
| private String tableDDLOptions; |
| private String transactionProvider; |
| |
| public StatsEnabledSplitSystemCatalogIT(String transactionProvider) { |
| StringBuilder optionBuilder = new StringBuilder(); |
| this.transactionProvider = transactionProvider; |
| if (transactionProvider != null) { |
| optionBuilder.append(" TRANSACTIONAL=true, TRANSACTION_PROVIDER='" + transactionProvider + "'"); |
| } |
| this.tableDDLOptions = optionBuilder.toString(); |
| } |
| |
| @Parameters(name = "transactionProvider = {0}") |
| public static Collection<Object[]> data() { |
| return TestUtil.filterTxParamData(Arrays.asList(new Object[][] { |
| { "TEPHRA" }, |
| { "OMID" }, |
| { null }}),0); |
| } |
| |
| @BeforeClass |
| public static synchronized void doSetup() throws Exception { |
| NUM_SLAVES_BASE = 3; |
| Map<String, String> props = Maps.newHashMapWithExpectedSize(1); |
| props.put(QueryServices.STATS_GUIDEPOST_WIDTH_BYTES_ATTRIB, Long.toString(20)); |
| props.put(QueryServices.STATS_UPDATE_FREQ_MS_ATTRIB, Long.toString(5)); |
| props.put(QueryServices.USE_STATS_FOR_PARALLELIZATION, Boolean.toString(true)); |
| setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator())); |
| } |
| |
| /** |
| * Salted tests must be in their own test file to ensure that the underlying |
| * table is dropped. Otherwise, the splits may not be performed. |
| * TODO: we should throw in that case |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testSaltedUpdatableViewWithIndex() throws Exception { |
| testUpdatableViewWithIndex(3, false); |
| } |
| |
| @Test |
| public void testSaltedUpdatableViewWithLocalIndex() throws Exception { |
| if (transactionProvider == null || |
| !TransactionFactory.getTransactionProvider( |
| TransactionFactory.Provider.valueOf(transactionProvider)).isUnsupported(Feature.ALLOW_LOCAL_INDEX)) { |
| testUpdatableViewWithIndex(3, true); |
| } |
| } |
| |
| @Test |
| public void testNonSaltedUpdatableViewWithIndex() throws Exception { |
| testUpdatableViewWithIndex(null, false); |
| } |
| |
| @Test |
| public void testNonSaltedUpdatableViewWithLocalIndex() throws Exception { |
| if (transactionProvider == null || |
| !TransactionFactory.getTransactionProvider( |
| TransactionFactory.Provider.valueOf(transactionProvider)).isUnsupported(Feature.ALLOW_LOCAL_INDEX)) { |
| testUpdatableViewWithIndex(null, true); |
| } |
| } |
| |
| @Test |
| public void testUpdatableOnUpdatableView() throws Exception { |
| String fullTableName = SchemaUtil.getTableName(generateUniqueName(), generateUniqueName()); |
| String fullViewName1 = SchemaUtil.getTableName(generateUniqueName(), generateUniqueName()); |
| String fullViewName2 = SchemaUtil.getTableName(generateUniqueName(), generateUniqueName()); |
| String ddl = "CREATE VIEW " + fullViewName2 + " AS SELECT * FROM " + fullViewName1 + " WHERE k3 = 2"; |
| ViewIT.testUpdatableView(fullTableName, fullViewName1, fullViewName2, ddl, null, tableDDLOptions); |
| Connection conn = DriverManager.getConnection(getUrl()); |
| ResultSet rs = conn.createStatement().executeQuery("SELECT k1, k2, k3 FROM " + fullViewName2); |
| assertTrue(rs.next()); |
| assertEquals(1, rs.getInt(1)); |
| assertEquals(109, rs.getInt(2)); |
| assertEquals(2, rs.getInt(3)); |
| assertFalse(rs.next()); |
| |
| conn.createStatement().execute("UPSERT INTO " + fullViewName2 + "(k2) VALUES(122)"); |
| conn.commit(); |
| rs = conn.createStatement().executeQuery("SELECT k1, k2, k3 FROM " + fullViewName2 + " WHERE k2 >= 120"); |
| assertTrue(rs.next()); |
| assertEquals(1, rs.getInt(1)); |
| assertEquals(122, rs.getInt(2)); |
| assertEquals(2, rs.getInt(3)); |
| assertFalse(rs.next()); |
| |
| try { |
| conn.createStatement().execute("UPSERT INTO " + fullViewName2 + "(k2,k3) VALUES(123,3)"); |
| fail(); |
| } catch (SQLException e) { |
| assertEquals(SQLExceptionCode.CANNOT_UPDATE_VIEW_COLUMN.getErrorCode(), e.getErrorCode()); |
| } |
| |
| try { |
| conn.createStatement().execute("UPSERT INTO " + fullViewName2 + "(k2,k3) select k2, 3 from " + fullViewName1); |
| fail(); |
| } catch (SQLException e) { |
| assertEquals(SQLExceptionCode.CANNOT_UPDATE_VIEW_COLUMN.getErrorCode(), e.getErrorCode()); |
| } |
| } |
| |
| private void testUpdatableViewWithIndex(Integer saltBuckets, boolean localIndex) throws Exception { |
| String schemaName = TestUtil.DEFAULT_SCHEMA_NAME + "_" + generateUniqueName(); |
| String tableName = "T_" + generateUniqueName(); |
| String fullTableName = SchemaUtil.getTableName(schemaName, tableName); |
| String viewName = "V_" + generateUniqueName(); |
| ViewIT.testUpdatableView(fullTableName, viewName, null, null, saltBuckets, tableDDLOptions); |
| Pair<String, Scan> pair = ViewIT.testUpdatableViewIndex(fullTableName, saltBuckets, localIndex, viewName); |
| Scan scan = pair.getSecond(); |
| String physicalTableName = pair.getFirst(); |
| // Confirm that dropping the view also deletes the rows in the index |
| if (saltBuckets == null) { |
| try (Connection conn = DriverManager.getConnection(getUrl())) { |
| Table htable = conn.unwrap(PhoenixConnection.class).getQueryServices() |
| .getTable(Bytes.toBytes(physicalTableName)); |
| if (ScanUtil.isLocalIndex(scan)) { |
| ScanUtil.setLocalIndexAttributes(scan, 0, HConstants.EMPTY_BYTE_ARRAY, HConstants.EMPTY_BYTE_ARRAY, |
| scan.getStartRow(), scan.getStopRow()); |
| } |
| ResultScanner scanner = htable.getScanner(scan); |
| Result result = scanner.next(); |
| // Confirm index has rows |
| assertTrue(result != null && !result.isEmpty()); |
| |
| conn.createStatement().execute("DROP VIEW " + viewName); |
| |
| // Confirm index has no rows after view is dropped |
| scanner = htable.getScanner(scan); |
| result = scanner.next(); |
| assertTrue(result == null || result.isEmpty()); |
| } |
| } |
| } |
| |
| @Test |
| public void testReadOnlyOnReadOnlyView() throws Exception { |
| Connection earlierCon = DriverManager.getConnection(getUrl()); |
| Connection conn = DriverManager.getConnection(getUrl()); |
| String fullTableName = SchemaUtil.getTableName(generateUniqueName(), generateUniqueName()); |
| String fullParentViewName = SchemaUtil.getTableName(generateUniqueName(), generateUniqueName()); |
| String fullViewName = SchemaUtil.getTableName(generateUniqueName(), generateUniqueName()); |
| |
| String ddl = "CREATE TABLE " + fullTableName + " (k INTEGER NOT NULL PRIMARY KEY, v1 DATE) "+ tableDDLOptions; |
| conn.createStatement().execute(ddl); |
| ddl = "CREATE VIEW " + fullParentViewName + " (v2 VARCHAR) AS SELECT * FROM " + fullTableName + " WHERE k > 5"; |
| conn.createStatement().execute(ddl); |
| ddl = "CREATE VIEW " + fullViewName + " AS SELECT * FROM " + fullParentViewName + " WHERE k < 9"; |
| conn.createStatement().execute(ddl); |
| |
| try { |
| conn.createStatement().execute("UPSERT INTO " + fullParentViewName + " VALUES(1)"); |
| fail(); |
| } catch (ReadOnlyTableException e) { |
| |
| } |
| for (int i = 0; i < 10; i++) { |
| conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES(" + i + ")"); |
| } |
| conn.commit(); |
| |
| analyzeTable(conn, fullParentViewName, transactionProvider != null); |
| |
| List<KeyRange> splits = getAllSplits(conn, fullParentViewName); |
| assertEquals(4, splits.size()); |
| |
| int count = 0; |
| ResultSet rs = conn.createStatement().executeQuery("SELECT k FROM " + fullTableName); |
| while (rs.next()) { |
| assertEquals(count++, rs.getInt(1)); |
| } |
| assertEquals(10, count); |
| |
| count = 0; |
| rs = conn.createStatement().executeQuery("SELECT k FROM " + fullParentViewName); |
| while (rs.next()) { |
| count++; |
| assertEquals(count + 5, rs.getInt(1)); |
| } |
| assertEquals(4, count); |
| count = 0; |
| rs = earlierCon.createStatement().executeQuery("SELECT k FROM " + fullParentViewName); |
| while (rs.next()) { |
| count++; |
| assertEquals(count + 5, rs.getInt(1)); |
| } |
| assertEquals(4, count); |
| |
| try { |
| conn.createStatement().execute("UPSERT INTO " + fullViewName + " VALUES(1)"); |
| fail(); |
| } catch (ReadOnlyTableException e) { |
| |
| } finally { |
| conn.close(); |
| } |
| |
| conn = DriverManager.getConnection(getUrl()); |
| count = 0; |
| rs = conn.createStatement().executeQuery("SELECT k FROM " + fullViewName); |
| while (rs.next()) { |
| count++; |
| assertEquals(count + 5, rs.getInt(1)); |
| } |
| assertEquals(3, count); |
| } |
| |
| } |