Repository: hive Updated Branches: refs/heads/standalone-metastore b62300014 -> c4d22858c (forced update)
HIVE-18580: Create tests to cover exchange partitions (Marta Kuczora, reviewed by Adam Szita, Peter Vary) Project: http://git-wip-us.apache.org/repos/asf/hive/repo Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/58bbfc73 Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/58bbfc73 Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/58bbfc73 Branch: refs/heads/standalone-metastore Commit: 58bbfc733939f5fe2229af106809270e3d6fb4e2 Parents: 6155f30 Author: Peter Vary <pv...@cloudera.com> Authored: Fri Feb 9 15:29:53 2018 +0100 Committer: Peter Vary <pv...@cloudera.com> Committed: Fri Feb 9 15:29:53 2018 +0100 ---------------------------------------------------------------------- .../client/TestExchangePartitions.java | 1502 ++++++++++++++++++ 1 file changed, 1502 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hive/blob/58bbfc73/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestExchangePartitions.java ---------------------------------------------------------------------- diff --git a/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestExchangePartitions.java b/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestExchangePartitions.java new file mode 100644 index 0000000..5a7aeb7 --- /dev/null +++ b/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestExchangePartitions.java @@ -0,0 +1,1502 @@ +/* + * 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.hadoop.hive.metastore.client; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hive.metastore.IMetaStoreClient; +import org.apache.hadoop.hive.metastore.Warehouse; +import org.apache.hadoop.hive.metastore.api.Database; +import org.apache.hadoop.hive.metastore.api.FieldSchema; +import org.apache.hadoop.hive.metastore.api.MetaException; +import org.apache.hadoop.hive.metastore.api.NoSuchObjectException; +import org.apache.hadoop.hive.metastore.api.Partition; +import org.apache.hadoop.hive.metastore.api.Table; +import org.apache.hadoop.hive.metastore.client.builder.DatabaseBuilder; +import org.apache.hadoop.hive.metastore.client.builder.PartitionBuilder; +import org.apache.hadoop.hive.metastore.client.builder.TableBuilder; +import org.apache.hadoop.hive.metastore.minihms.AbstractMetaStoreService; +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransportException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import com.google.common.collect.Lists; + +/** + * Tests for exchanging partitions. + */ +@RunWith(Parameterized.class) +public class TestExchangePartitions { + + // Needed until there is no junit release with @BeforeParam, @AfterParam (junit 4.13) + // https://github.com/junit-team/junit4/commit/1bf8438b65858565dbb64736bfe13aae9cfc1b5a + // Then we should remove our own copy + private static Set<AbstractMetaStoreService> metaStoreServices = null; + private AbstractMetaStoreService metaStore; + private IMetaStoreClient client; + + private static final String DB_NAME = "test_partition_db"; + private static final String STRING_COL_TYPE = "string"; + private static final String INT_COL_TYPE = "int"; + private static final String YEAR_COL_NAME = "year"; + private static final String MONTH_COL_NAME = "month"; + private static final String DAY_COL_NAME = "day"; + private static final short MAX = -1; + private static Table sourceTable; + private static Table destTable; + private static Partition[] partitions; + + + @Parameterized.Parameters(name = "{0}") + public static List<Object[]> getMetaStoreToTest() throws Exception { + List<Object[]> result = MetaStoreFactoryForTests.getMetaStores(); + metaStoreServices = result.stream() + .map(test -> (AbstractMetaStoreService)test[1]) + .collect(Collectors.toSet()); + return result; + } + + public TestExchangePartitions(String name, AbstractMetaStoreService metaStore) throws Exception { + this.metaStore = metaStore; + this.metaStore.start(); + } + + // Needed until there is no junit release with @BeforeParam, @AfterParam (junit 4.13) + // https://github.com/junit-team/junit4/commit/1bf8438b65858565dbb64736bfe13aae9cfc1b5a + // Then we should move this to @AfterParam + @AfterClass + public static void stopMetaStores() throws Exception { + for (AbstractMetaStoreService metaStoreService : metaStoreServices) { + metaStoreService.stop(); + } + } + + @Before + public void setUp() throws Exception { + // Get new client + client = metaStore.getClient(); + + // Clean up the database + client.dropDatabase(DB_NAME, true, true, true); + metaStore.cleanWarehouseDirs(); + createDB(DB_NAME); + sourceTable = createSourceTable(); + destTable = createDestTable(); + partitions = createTestPartitions(); + } + + @After + public void tearDown() throws Exception { + try { + if (client != null) { + client.close(); + } + } finally { + client = null; + } + } + + // Tests for the List<Partition> exchange_partitions(Map<String, String> partitionSpecs, String + // sourceDb, String sourceTable, String destdb, String destTableName) method + + @Test + public void testExchangePartitions() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + List<Partition> exchangedPartitions = + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + + Assert.assertEquals(1, exchangedPartitions.size()); + String partitionName = + Warehouse.makePartName(sourceTable.getPartitionKeys(), partitions[1].getValues()); + String exchangedPartitionName = Warehouse.makePartName(sourceTable.getPartitionKeys(), + exchangedPartitions.get(0).getValues()); + Assert.assertEquals(partitionName, exchangedPartitionName); + + checkExchangedPartitions(sourceTable, destTable, Lists.newArrayList(partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionsDestTableHasPartitions() throws Exception { + + // Create dest table partitions with custom locations + createPartition(destTable, Lists.newArrayList("2019", "march", "15"), + destTable.getSd().getLocation() + "/destPart1"); + createPartition(destTable, Lists.newArrayList("2019", "march", "22"), + destTable.getSd().getLocation() + "/destPart2"); + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + + client.exchange_partitions(partitionSpecs, DB_NAME, sourceTable.getTableName(), DB_NAME, + destTable.getTableName()); + + checkExchangedPartitions(sourceTable, destTable, Lists.newArrayList(partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + // Check the original partitions of the dest table + List<String> partitionNames = + client.listPartitionNames(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertEquals(3, partitionNames.size()); + Assert.assertTrue(partitionNames.containsAll( + Lists.newArrayList("year=2019/month=march/day=15", "year=2019/month=march/day=22"))); + Assert.assertTrue( + metaStore.isPathExists(new Path(destTable.getSd().getLocation() + "/destPart1"))); + Assert.assertTrue( + metaStore.isPathExists(new Path(destTable.getSd().getLocation() + "/destPart2"))); + } + + @Test + public void testExchangePartitionsYearSet() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "", "")); + List<Partition> exchangedPartitions = + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + + Assert.assertEquals(4, exchangedPartitions.size()); + List<String> exchangedPartNames = new ArrayList<>(); + for (Partition exchangedPartition : exchangedPartitions) { + String partName = + Warehouse.makePartName(sourceTable.getPartitionKeys(), exchangedPartition.getValues()); + exchangedPartNames.add(partName); + } + Assert.assertTrue(exchangedPartNames.contains("year=2017/month=march/day=15")); + Assert.assertTrue(exchangedPartNames.contains("year=2017/month=march/day=22")); + Assert.assertTrue(exchangedPartNames.contains("year=2017/month=april/day=23")); + Assert.assertTrue(exchangedPartNames.contains("year=2017/month=may/day=23")); + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1], partitions[2], partitions[3])); + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[4])); + } + + @Test + public void testExchangePartitionsYearAndMonthSet() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "march", "")); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionsBetweenDBs() throws Exception { + + String dbName = "newDatabase"; + createDB(dbName); + Table dest = createTable(dbName, "test_dest_table_diff_db", getYearMonthAndDayPartCols(), null); + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "march", "")); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + + checkExchangedPartitions(sourceTable, dest, Lists.newArrayList(partitions[0], partitions[1])); + checkRemainingPartitions(sourceTable, dest, + Lists.newArrayList(partitions[2], partitions[3], partitions[4])); + client.dropDatabase(dbName, true, true, true); + } + + @Test + public void testExchangePartitionsCustomTableLocations() throws Exception { + + Table source = createTable(DB_NAME, "test_source_table_cust_loc", getYearMonthAndDayPartCols(), + metaStore.getWarehouseRoot() + "/sourceTable"); + Table dest = createTable(DB_NAME, "test_dest_table_cust_loc", getYearMonthAndDayPartCols(), + metaStore.getWarehouseRoot() + "/destTable"); + Partition[] parts = new Partition[2]; + parts[0] = createPartition(source, Lists.newArrayList("2019", "may", "15"), null); + parts[1] = createPartition(source, Lists.newArrayList("2019", "june", "14"), null); + + Map<String, String> partitionSpecs = getPartitionSpec(parts[1]); + + client.exchange_partitions(partitionSpecs, source.getDbName(), source.getTableName(), + dest.getDbName(), dest.getTableName()); + + checkExchangedPartitions(source, dest, Lists.newArrayList(parts[1])); + checkRemainingPartitions(source, dest, Lists.newArrayList(parts[0])); + } + + @Test + public void testExchangePartitionsCustomTableAndPartLocation() throws Exception { + + Table source = createTable(DB_NAME, "test_source_table_cust_loc", + getYearMonthAndDayPartCols(), metaStore.getWarehouseRoot() + "/sourceTable"); + Table dest = createTable(DB_NAME, "test_dest_table_cust_loc", getYearMonthAndDayPartCols(), + metaStore.getWarehouseRoot() + "/destTable"); + Partition[] parts = new Partition[2]; + parts[0] = createPartition(source, Lists.newArrayList("2019", "may", "11"), + source.getSd().getLocation() + "/2019m11"); + parts[1] = createPartition(source, Lists.newArrayList("2019", "july", "23"), + source.getSd().getLocation() + "/2019j23"); + + Map<String, String> partitionSpecs = getPartitionSpec(parts[1]); + try { + client.exchange_partitions(partitionSpecs, source.getDbName(), + source.getTableName(), dest.getDbName(), dest.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception as FileNotFoundException will occur if the partitions have custom + // location + } + + checkRemainingPartitions(source, dest, + Lists.newArrayList(parts[0], parts[1])); + List<Partition> destTablePartitions = + client.listPartitions(dest.getDbName(), dest.getTableName(), (short) -1); + Assert.assertTrue(destTablePartitions.isEmpty()); + } + + @Test + public void testExchangePartitionsCustomPartLocation() throws Exception { + + Table source = createTable(DB_NAME, "test_source_table", getYearMonthAndDayPartCols(), null); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), null); + Partition[] parts = new Partition[2]; + parts[0] = createPartition(source, Lists.newArrayList("2019", "march", "15"), + source.getSd().getLocation() + "/2019m15"); + parts[1] = createPartition(source, Lists.newArrayList("2019", "march", "22"), + source.getSd().getLocation() + "/2019m22"); + + Map<String, String> partitionSpecs = getPartitionSpec(parts[1]); + try { + client.exchange_partitions(partitionSpecs, source.getDbName(), + source.getTableName(), dest.getDbName(), dest.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception as FileNotFoundException will occur if the partitions have custom + // location + } + + checkRemainingPartitions(source, dest, + Lists.newArrayList(parts[0], parts[1])); + List<Partition> destTablePartitions = + client.listPartitions(dest.getDbName(), dest.getTableName(), (short) -1); + Assert.assertTrue(destTablePartitions.isEmpty()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsNonExistingPartLocation() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + metaStore.cleanWarehouseDirs(); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test + public void testExchangePartitionsNonExistingSourceTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, DB_NAME, "nonexistingtable", + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNonExistingSourceDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, "nonexistingdb", sourceTable.getTableName(), + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNonExistingDestTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), DB_NAME, "nonexistingtable"); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNonExistingDestDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), "nonexistingdb", destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsEmptySourceTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, DB_NAME, "", destTable.getDbName(), + destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsEmptySourceDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, "", sourceTable.getTableName(), + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsEmptyDestTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), DB_NAME, ""); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsEmptyDestDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), "", destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNullSourceTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, DB_NAME, null, destTable.getDbName(), + destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNullSourceDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, null, sourceTable.getTableName(), + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNullDestTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), DB_NAME, null); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionsNullDestDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), null, destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsEmptyPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test + public void testExchangePartitionsNullPartSpec() throws Exception { + try { + client.exchange_partitions(null, sourceTable.getDbName(), sourceTable.getTableName(), null, + destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: NPE should not be thrown + } + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsPartAlreadyExists() throws Exception { + + Partition partition = + buildPartition(destTable, Lists.newArrayList("2017", "march", "22"), null); + client.add_partition(partition); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, DB_NAME, sourceTable.getTableName(), DB_NAME, + destTable.getTableName()); + // TODO: In this case we get an exception, because the insert statement fails due to duplicated + // primary key. Maybe this use case could be handled better by checking if the partition already + // exists. + } + + @Test + public void testExchangePartitionsOneFail() throws Exception { + + Partition partition = + buildPartition(destTable, Lists.newArrayList("2017", "march", "22"), null); + client.add_partition(partition); + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "", "")); + try { + client.exchange_partitions(partitionSpecs, DB_NAME, sourceTable.getTableName(), DB_NAME, + destTable.getTableName()); + Assert.fail( + "Exception should have been thrown as one of the partitions already exists in the dest table."); + } catch (MetaException e) { + // Expected exception + } + + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + List<Partition> partitionsInDestTable = + client.listPartitions(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertEquals(1, partitionsInDestTable.size()); + Assert.assertEquals(partitions[1].getValues(), partitionsInDestTable.get(0).getValues()); + Assert.assertTrue( + metaStore.isPathExists(new Path(partitionsInDestTable.get(0).getSd().getLocation()))); + Partition resultPart = client.getPartition(sourceTable.getDbName(), + sourceTable.getTableName(), partitions[1].getValues()); + Assert.assertNotNull(resultPart); + Assert.assertTrue(metaStore.isPathExists(new Path(partitions[1].getSd().getLocation()))); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsDifferentColsInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("test_id", INT_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", STRING_COL_TYPE, "test col value")); + cols.add(new FieldSchema("test_name", STRING_COL_TYPE, "test col name")); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsDifferentColNameInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("id", INT_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", STRING_COL_TYPE, "test col value")); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsDifferentColTypesInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("test_id", STRING_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", STRING_COL_TYPE, "test col value")); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsDifferentPartColsInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, STRING_COL_TYPE, "month part col")); + Table dest = createTable(DB_NAME, "test_dest_table", cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsDifferentPartColNameInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, STRING_COL_TYPE, "month part col")); + cols.add(new FieldSchema("nap", STRING_COL_TYPE, "day part col")); + Table dest = createTable(DB_NAME, "test_dest_table", cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsDifferentPartColTypesInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, INT_COL_TYPE, "month part col")); + cols.add(new FieldSchema(DAY_COL_NAME, STRING_COL_TYPE, "day part col")); + Table dest = createTable(DB_NAME, "test_dest_table", cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), dest.getDbName(), dest.getTableName()); + } + + @Test + public void testExchangePartitionsLessValueInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put(MONTH_COL_NAME, "march"); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionsMoreValueInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put(MONTH_COL_NAME, "march"); + partitionSpecs.put(DAY_COL_NAME, "22"); + partitionSpecs.put("hour", "18"); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + checkExchangedPartitions(sourceTable, destTable, Lists.newArrayList(partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionsDifferentValuesInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put("honap", "march"); + partitionSpecs.put("nap", "22"); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1], partitions[2], partitions[3])); + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[4])); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsNonExistingValuesInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put("ev", "2017"); + partitionSpecs.put("honap", "march"); + partitionSpecs.put("nap", "22"); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test + public void testExchangePartitionsOnlyMonthSetInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, ""); + partitionSpecs.put(MONTH_COL_NAME, "march"); + partitionSpecs.put(DAY_COL_NAME, ""); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception + } + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[0], + partitions[1], partitions[2], partitions[3], partitions[4])); + List<Partition> partsInDestTable = + client.listPartitions(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertTrue(partsInDestTable.isEmpty()); + } + + @Test + public void testExchangePartitionsYearAndDaySetInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put(MONTH_COL_NAME, ""); + partitionSpecs.put(DAY_COL_NAME, "22"); + try { + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception + } + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[0], + partitions[1], partitions[2], partitions[3], partitions[4])); + List<Partition> partsInDestTable = + client.listPartitions(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertTrue(partsInDestTable.isEmpty()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsNoPartExists() throws Exception { + + // TODO: FileNotFoundException will occur. This use case could be handled in a better way like + // checking if the partitionsToExchange is empty and throw an exception if it is. + Map<String, String> partitionSpecs = + getPartitionSpec(Lists.newArrayList("2017", "march", "25")); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionsNoPartExistsYearAndMonthSet() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "august", "")); + client.exchange_partitions(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + // Tests for the Partition exchange_partition(Map<String, String> partitionSpecs, String + // sourceDb, String sourceTable, String destdb, String destTableName) method + + @Test + public void testExchangePartition() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + Partition exchangedPartition = + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + + Assert.assertEquals(new Partition(), exchangedPartition); + checkExchangedPartitions(sourceTable, destTable, Lists.newArrayList(partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionDestTableHasPartitions() throws Exception { + + // Create dest table partitions with custom locations + createPartition(destTable, Lists.newArrayList("2019", "march", "15"), + destTable.getSd().getLocation() + "/destPart1"); + createPartition(destTable, Lists.newArrayList("2019", "march", "22"), + destTable.getSd().getLocation() + "/destPart2"); + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + + client.exchange_partition(partitionSpecs, DB_NAME, sourceTable.getTableName(), DB_NAME, + destTable.getTableName()); + + checkExchangedPartitions(sourceTable, destTable, Lists.newArrayList(partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + // Check the original partitions of the dest table + List<String> partitionNames = + client.listPartitionNames(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertEquals(3, partitionNames.size()); + Assert.assertTrue(partitionNames.containsAll( + Lists.newArrayList("year=2019/month=march/day=15", "year=2019/month=march/day=22"))); + Assert.assertTrue( + metaStore.isPathExists(new Path(destTable.getSd().getLocation() + "/destPart1"))); + Assert.assertTrue( + metaStore.isPathExists(new Path(destTable.getSd().getLocation() + "/destPart2"))); + } + + @Test + public void testExchangePartitionYearSet() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "", "")); + Partition exchangedPartition = + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + + Assert.assertEquals(new Partition(), exchangedPartition); + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1], partitions[2], partitions[3])); + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[4])); + } + + @Test + public void testExchangePartitionYearAndMonthSet() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "march", "")); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionBetweenDBs() throws Exception { + + String dbName = "newDatabase"; + createDB(dbName); + Table dest = createTable(dbName, "test_dest_table_diff_db", getYearMonthAndDayPartCols(), null); + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "march", "")); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + + checkExchangedPartitions(sourceTable, dest, Lists.newArrayList(partitions[0], partitions[1])); + checkRemainingPartitions(sourceTable, dest, + Lists.newArrayList(partitions[2], partitions[3], partitions[4])); + client.dropDatabase(dbName, true, true, true); + } + + @Test + public void testExchangePartitionCustomTableLocations() throws Exception { + + Table source = createTable(DB_NAME, "test_source_table_cust_loc", + getYearMonthAndDayPartCols(), metaStore.getWarehouseRoot() + "/sourceTable"); + Table dest = createTable(DB_NAME, "test_dest_table_cust_loc", getYearMonthAndDayPartCols(), + metaStore.getWarehouseRoot() + "/destTable"); + Partition[] parts = new Partition[2]; + parts[0] = createPartition(source, Lists.newArrayList("2019", "may", "15"), null); + parts[1] = createPartition(source, Lists.newArrayList("2019", "june", "14"), null); + + Map<String, String> partitionSpecs = getPartitionSpec(parts[1]); + + client.exchange_partition(partitionSpecs, source.getDbName(), source.getTableName(), + dest.getDbName(), dest.getTableName()); + + checkExchangedPartitions(source, dest, Lists.newArrayList(parts[1])); + checkRemainingPartitions(source, dest, Lists.newArrayList(parts[0])); + } + + @Test + public void testExchangePartitionCustomTableAndPartLocation() throws Exception { + + Table source = createTable(DB_NAME, "test_source_table_cust_loc", + getYearMonthAndDayPartCols(), metaStore.getWarehouseRoot() + "/sourceTable"); + Table dest = createTable(DB_NAME, "test_dest_table_cust_loc", getYearMonthAndDayPartCols(), + metaStore.getWarehouseRoot() + "/destTable"); + Partition[] parts = new Partition[2]; + parts[0] = createPartition(source, Lists.newArrayList("2019", "may", "11"), + source.getSd().getLocation() + "/2019m11"); + parts[1] = createPartition(source, Lists.newArrayList("2019", "july", "23"), + source.getSd().getLocation() + "/2019j23"); + + Map<String, String> partitionSpecs = getPartitionSpec(parts[1]); + try { + client.exchange_partition(partitionSpecs, source.getDbName(), + source.getTableName(), dest.getDbName(), dest.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception as FileNotFoundException will occur if the partitions have custom + // location + } + + checkRemainingPartitions(source, dest, + Lists.newArrayList(parts[0], parts[1])); + List<Partition> destTablePartitions = + client.listPartitions(dest.getDbName(), dest.getTableName(), (short) -1); + Assert.assertTrue(destTablePartitions.isEmpty()); + } + + @Test + public void testExchangePartitionCustomPartLocation() throws Exception { + + Table source = createTable(DB_NAME, "test_source_table", getYearMonthAndDayPartCols(), null); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), null); + Partition[] parts = new Partition[2]; + parts[0] = createPartition(source, Lists.newArrayList("2019", "march", "15"), + source.getSd().getLocation() + "/2019m15"); + parts[1] = createPartition(source, Lists.newArrayList("2019", "march", "22"), + source.getSd().getLocation() + "/2019m22"); + + Map<String, String> partitionSpecs = getPartitionSpec(parts[1]); + try { + client.exchange_partition(partitionSpecs, source.getDbName(), + source.getTableName(), dest.getDbName(), dest.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception as FileNotFoundException will occur if the partitions have custom + // location + } + + checkRemainingPartitions(source, dest, + Lists.newArrayList(parts[0], parts[1])); + List<Partition> destTablePartitions = + client.listPartitions(dest.getDbName(), dest.getTableName(), (short) -1); + Assert.assertTrue(destTablePartitions.isEmpty()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionNonExistingPartLocation() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + metaStore.cleanWarehouseDirs(); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test + public void testExchangePartitionNonExistingSourceTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, DB_NAME, "nonexistingtable", + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNonExistingSourceDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, "nonexistingdb", sourceTable.getTableName(), + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNonExistingDestTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), DB_NAME, "nonexistingtable"); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNonExistingDestDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), "nonexistingdb", destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionEmptySourceTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, DB_NAME, "", destTable.getDbName(), + destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionEmptySourceDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, "", sourceTable.getTableName(), + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionEmptyDestTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), DB_NAME, ""); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionEmptyDestDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), "", destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNullSourceTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, DB_NAME, null, destTable.getDbName(), + destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNullSourceDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, null, sourceTable.getTableName(), + destTable.getDbName(), destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNullDestTable() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), DB_NAME, null); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test + public void testExchangePartitionNullDestDB() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), null, destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: Non existing table or db should be handled correctly and NPE should not occur. + } + } + + @Test(expected = MetaException.class) + public void testExchangePartitionEmptyPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test + public void testExchangePartitionNullPartSpec() throws Exception { + try { + client.exchange_partition(null, sourceTable.getDbName(), sourceTable.getTableName(), null, + destTable.getTableName()); + Assert.fail("Exception should have been thrown."); + } catch (TTransportException | NullPointerException e) { + // TODO: NPE should not be thrown + } + } + + @Test(expected = MetaException.class) + public void testExchangePartitionPartAlreadyExists() throws Exception { + + Partition partition = + buildPartition(destTable, Lists.newArrayList("2017", "march", "22"), null); + client.add_partition(partition); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, DB_NAME, sourceTable.getTableName(), DB_NAME, + destTable.getTableName()); + // TODO: In this case we get an exception, because the insert statement fails due to duplicated + // primary key. Maybe this use case could be handled better by checking if the partition already + // exists. + } + + @Test + public void testExchangePartitionOneFail() throws Exception { + + Partition partition = + buildPartition(destTable, Lists.newArrayList("2017", "march", "22"), null); + client.add_partition(partition); + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "", "")); + try { + client.exchange_partition(partitionSpecs, DB_NAME, sourceTable.getTableName(), DB_NAME, + destTable.getTableName()); + Assert.fail( + "Exception should have been thrown as one of the partitions already exists in the dest table."); + } catch (MetaException e) { + // Expected exception + } + + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + List<Partition> partitionsInDestTable = + client.listPartitions(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertEquals(1, partitionsInDestTable.size()); + Assert.assertEquals(partitions[1].getValues(), partitionsInDestTable.get(0).getValues()); + Assert.assertTrue( + metaStore.isPathExists(new Path(partitionsInDestTable.get(0).getSd().getLocation()))); + Partition resultPart = client.getPartition(sourceTable.getDbName(), + sourceTable.getTableName(), partitions[1].getValues()); + Assert.assertNotNull(resultPart); + Assert.assertTrue(metaStore.isPathExists(new Path(partitions[1].getSd().getLocation()))); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionDifferentColsInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("test_id", INT_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", STRING_COL_TYPE, "test col value")); + cols.add(new FieldSchema("test_name", STRING_COL_TYPE, "test col name")); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionDifferentColNameInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("id", INT_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", STRING_COL_TYPE, "test col value")); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionDifferentColTypesInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("test_id", STRING_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", STRING_COL_TYPE, "test col value")); + Table dest = createTable(DB_NAME, "test_dest_table", getYearMonthAndDayPartCols(), cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), sourceTable.getTableName(), + dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionDifferentPartColsInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, STRING_COL_TYPE, "month part col")); + Table dest = createTable(DB_NAME, "test_dest_table", cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionDifferentPartColNameInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, STRING_COL_TYPE, "month part col")); + cols.add(new FieldSchema("nap", STRING_COL_TYPE, "day part col")); + Table dest = createTable(DB_NAME, "test_dest_table", cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), dest.getDbName(), dest.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionDifferentPartColTypesInTables() throws Exception { + + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, INT_COL_TYPE, "month part col")); + cols.add(new FieldSchema(DAY_COL_NAME, STRING_COL_TYPE, "day part col")); + Table dest = createTable(DB_NAME, "test_dest_table", cols, null); + + Map<String, String> partitionSpecs = getPartitionSpec(partitions[1]); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), dest.getDbName(), dest.getTableName()); + } + + @Test + public void testExchangePartitionLessValueInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put(MONTH_COL_NAME, "march"); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionMoreValueInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put(MONTH_COL_NAME, "march"); + partitionSpecs.put(DAY_COL_NAME, "22"); + partitionSpecs.put("hour", "18"); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + checkExchangedPartitions(sourceTable, destTable, Lists.newArrayList(partitions[1])); + checkRemainingPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[2], partitions[3], partitions[4])); + } + + @Test + public void testExchangePartitionDifferentValuesInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put("honap", "march"); + partitionSpecs.put("nap", "22"); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + checkExchangedPartitions(sourceTable, destTable, + Lists.newArrayList(partitions[0], partitions[1], partitions[2], partitions[3])); + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[4])); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionNonExistingValuesInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put("ev", "2017"); + partitionSpecs.put("honap", "march"); + partitionSpecs.put("nap", "22"); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test + public void testExchangePartitionOnlyMonthSetInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, ""); + partitionSpecs.put(MONTH_COL_NAME, "march"); + partitionSpecs.put(DAY_COL_NAME, ""); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception + } + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[0], + partitions[1], partitions[2], partitions[3], partitions[4])); + List<Partition> partsInDestTable = + client.listPartitions(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertTrue(partsInDestTable.isEmpty()); + } + + @Test + public void testExchangePartitionYearAndDaySetInPartSpec() throws Exception { + + Map<String, String> partitionSpecs = new HashMap<>(); + partitionSpecs.put(YEAR_COL_NAME, "2017"); + partitionSpecs.put(MONTH_COL_NAME, ""); + partitionSpecs.put(DAY_COL_NAME, "22"); + try { + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + Assert.fail("MetaException should have been thrown."); + } catch (MetaException e) { + // Expected exception + } + checkRemainingPartitions(sourceTable, destTable, Lists.newArrayList(partitions[0], + partitions[1], partitions[2], partitions[3], partitions[4])); + List<Partition> partsInDestTable = + client.listPartitions(destTable.getDbName(), destTable.getTableName(), MAX); + Assert.assertTrue(partsInDestTable.isEmpty()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionNoPartExists() throws Exception { + + // TODO: FileNotFoundException will occur. This use case could be handled in a better way like + // checking if the partitionsToExchange is empty and throw an exception if it is. + Map<String, String> partitionSpecs = + getPartitionSpec(Lists.newArrayList("2017", "march", "25")); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + @Test(expected = MetaException.class) + public void testExchangePartitionNoPartExistsYearAndMonthSet() throws Exception { + + Map<String, String> partitionSpecs = getPartitionSpec(Lists.newArrayList("2017", "august", "")); + client.exchange_partition(partitionSpecs, sourceTable.getDbName(), + sourceTable.getTableName(), destTable.getDbName(), destTable.getTableName()); + } + + // Helper methods + private void createDB(String dbName) throws TException { + Database db = new DatabaseBuilder() + .setName(dbName) + .build(); + client.createDatabase(db); + } + + private Table createSourceTable() throws Exception { + return createTable(DB_NAME, "test_part_exch_table_source", getYearMonthAndDayPartCols(), null); + } + + private Table createDestTable() throws Exception { + return createTable(DB_NAME, "test_part_exch_table_dest", getYearMonthAndDayPartCols(), null); + } + + private Table createTable(String dbName, String tableName, List<FieldSchema> partCols, + String location) throws Exception { + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema("test_id", INT_COL_TYPE, "test col id")); + cols.add(new FieldSchema("test_value", "string", "test col value")); + return createTable(dbName, tableName, partCols, cols, location); + } + + private Table createTable(String dbName, String tableName, List<FieldSchema> partCols, + List<FieldSchema> cols, String location) throws Exception { + Table table = new TableBuilder() + .setDbName(dbName) + .setTableName(tableName) + .setCols(cols) + .setPartCols(partCols) + .setLocation(location) + .build(); + client.createTable(table); + return client.getTable(dbName, tableName); + } + + private Partition[] createTestPartitions() throws Exception { + Partition partition1 = + buildPartition(sourceTable, Lists.newArrayList("2017", "march", "15"), null); + Partition partition2 = + buildPartition(sourceTable, Lists.newArrayList("2017", "march", "22"), null); + Partition partition3 = + buildPartition(sourceTable, Lists.newArrayList("2017", "april", "23"), null); + Partition partition4 = + buildPartition(sourceTable, Lists.newArrayList("2017", "may", "23"), null); + Partition partition5 = + buildPartition(sourceTable, Lists.newArrayList("2018", "april", "23"), null); + client.add_partitions( + Lists.newArrayList(partition1, partition2, partition3, partition4, partition5)); + + Partition[] parts = new Partition[5]; + parts[0] = + client.getPartition(DB_NAME, sourceTable.getTableName(), partition1.getValues()); + parts[1] = + client.getPartition(DB_NAME, sourceTable.getTableName(), partition2.getValues()); + parts[2] = + client.getPartition(DB_NAME, sourceTable.getTableName(), partition3.getValues()); + parts[3] = + client.getPartition(DB_NAME, sourceTable.getTableName(), partition4.getValues()); + parts[4] = + client.getPartition(DB_NAME, sourceTable.getTableName(), partition5.getValues()); + return parts; + } + + private Partition createPartition(Table table, List<String> values, String location) + throws Exception { + Partition partition = buildPartition(table, values, location); + client.add_partition(partition); + return client.getPartition(DB_NAME, table.getTableName(), partition.getValues()); + } + + private Partition buildPartition(Table table, List<String> values, String location) + throws MetaException { + Partition partition = new PartitionBuilder() + .setDbName(table.getDbName()) + .setTableName(table.getTableName()) + .setValues(values) + .addPartParam("test_exch_param_key", "test_exch_param_value") + .setInputFormat("TestInputFormat") + .setOutputFormat("TestOutputFormat") + .addStorageDescriptorParam("test_exch_sd_param_key", "test_exch_sd_param_value") + .setCols(getYearMonthAndDayPartCols()) + .setLocation(location) + .build(); + return partition; + } + + private void checkExchangedPartitions(Table sourceTable, Table destTable, + List<Partition> partitions) throws Exception { + + for (Partition partition : partitions) { + // Check if the partitions exist in the destTable + Partition resultPart = client.getPartition(destTable.getDbName(), destTable.getTableName(), + partition.getValues()); + Assert.assertNotNull(resultPart); + Assert.assertEquals(destTable.getDbName(), resultPart.getDbName()); + Assert.assertEquals(destTable.getTableName(), resultPart.getTableName()); + // Check the location of the result partition. It should be located in the destination table + // folder. + String partName = + Warehouse.makePartName(sourceTable.getPartitionKeys(), partition.getValues()); + Assert.assertEquals(destTable.getSd().getLocation() + "/" + partName, + resultPart.getSd().getLocation()); + Assert.assertTrue(metaStore.isPathExists(new Path(resultPart.getSd().getLocation()))); + + // Check if the partitions don't exist in the sourceTable + try { + client.getPartition(sourceTable.getDbName(), sourceTable.getTableName(), + partition.getValues()); + Assert.fail("The partition ' " + partition.getValues().toString() + + " ' should not exists, therefore NoSuchObjectException should have been thrown."); + } catch (NoSuchObjectException e) { + // Expected exception + } + Assert.assertFalse(metaStore.isPathExists(new Path(partition.getSd().getLocation()))); + + // Reset the location, db and table name and compare the partitions + partition.getSd().setLocation(resultPart.getSd().getLocation()); + partition.setDbName(destTable.getDbName()); + partition.setTableName(destTable.getTableName()); + Assert.assertEquals(partition, resultPart); + } + } + + private void checkRemainingPartitions(Table sourceTable, Table destTable, + List<Partition> partitions) throws Exception { + + for (Partition partition : partitions) { + // Check if the partitions exist in the sourceTable + Partition resultPart = client.getPartition(sourceTable.getDbName(), + sourceTable.getTableName(), partition.getValues()); + Assert.assertNotNull(resultPart); + Assert.assertEquals(partition, resultPart); + Assert.assertTrue(metaStore.isPathExists(new Path(partition.getSd().getLocation()))); + + // Check if the partitions don't exist in the destTable + try { + client.getPartition(destTable.getDbName(), destTable.getTableName(), partition.getValues()); + Assert.fail("The partition '" + partition.getValues().toString() + + "'should not exists, therefore NoSuchObjectException should have been thrown."); + } catch (NoSuchObjectException e) { + // Expected exception + } + String partName = + Warehouse.makePartName(sourceTable.getPartitionKeys(), partition.getValues()); + Assert.assertFalse( + metaStore.isPathExists(new Path(destTable.getSd().getLocation() + "/" + partName))); + } + } + + private static Map<String, String> getPartitionSpec(Partition partition) { + return getPartitionSpec(partition.getValues()); + } + + private static Map<String, String> getPartitionSpec(List<String> values) { + Map<String, String> partitionSpecs = new HashMap<>(); + List<FieldSchema> partCols = getYearMonthAndDayPartCols(); + for (int i = 0; i < partCols.size(); i++) { + FieldSchema partCol = partCols.get(i); + String value = values.get(i); + partitionSpecs.put(partCol.getName(), value); + } + return partitionSpecs; + } + + private static List<FieldSchema> getYearMonthAndDayPartCols() { + List<FieldSchema> cols = new ArrayList<>(); + cols.add(new FieldSchema(YEAR_COL_NAME, STRING_COL_TYPE, "year part col")); + cols.add(new FieldSchema(MONTH_COL_NAME, STRING_COL_TYPE, "month part col")); + cols.add(new FieldSchema(DAY_COL_NAME, STRING_COL_TYPE, "day part col")); + return cols; + } +}