You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by pv...@apache.org on 2018/06/28 14:37:16 UTC
hive git commit: HIVE-19046: Refactor the common parts of the
HiveMetastore add_partition_core and add_partitions_pspec_core methods (Marta
Kuczora, reviewed by Peter Vary, Sahil Takiar and Alexander Kolbasov)
Repository: hive
Updated Branches:
refs/heads/master 22ca245f3 -> 49abcf767
HIVE-19046: Refactor the common parts of the HiveMetastore add_partition_core and add_partitions_pspec_core methods (Marta Kuczora, reviewed by Peter Vary, Sahil Takiar and Alexander Kolbasov)
Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/49abcf76
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/49abcf76
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/49abcf76
Branch: refs/heads/master
Commit: 49abcf767b1da0661e2f4b150c6f80d63cae2ce1
Parents: 22ca245
Author: Marta Kuczora <ku...@cloudera.com>
Authored: Thu Jun 28 16:36:05 2018 +0200
Committer: Peter Vary <pv...@cloudera.com>
Committed: Thu Jun 28 16:36:05 2018 +0200
----------------------------------------------------------------------
.../hadoop/hive/metastore/HiveMetaStore.java | 367 ++++++++++---------
.../metastore/client/TestAddPartitions.java | 86 +++++
.../client/TestAddPartitionsFromPartSpec.java | 96 +++++
3 files changed, 366 insertions(+), 183 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hive/blob/49abcf76/standalone-metastore/src/main/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java
----------------------------------------------------------------------
diff --git a/standalone-metastore/src/main/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java b/standalone-metastore/src/main/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java
index 35c0f5c..5819a26 100644
--- a/standalone-metastore/src/main/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java
+++ b/standalone-metastore/src/main/java/org/apache/hadoop/hive/metastore/HiveMetaStore.java
@@ -3290,102 +3290,21 @@ public class HiveMetaStore extends ThriftHiveMetastore {
Set<PartValEqWrapperLite> partsToAdd = new HashSet<>(parts.size());
List<Partition> partitionsToAdd = new ArrayList<>(parts.size());
+ List<FieldSchema> partitionKeys = tbl.getPartitionKeys();
for (final Partition part : parts) {
// Iterate through the partitions and validate them. If one of the partitions is
// incorrect, an exception will be thrown before the threads which create the partition
// folders are submitted. This way we can be sure that no partition and no partition
// folder will be created if the list contains an invalid partition.
- if (!part.getTableName().equals(tblName) || !part.getDbName().equals(dbName)) {
- String errorMsg = String.format(
- "Partition does not belong to target table %s. It belongs to the table %s.%s : %s",
- TableName.getQualified(catName, dbName, tblName), part.getDbName(),
- part.getTableName(), part.toString());
- throw new MetaException(errorMsg);
- }
- if (part.getValues() == null || part.getValues().isEmpty()) {
- throw new MetaException("The partition values cannot be null or empty.");
- }
- if (part.getValues().contains(null)) {
- throw new MetaException("Partition value cannot be null.");
- }
-
- boolean shouldAdd = startAddPartition(ms, part, tbl.getPartitionKeys(), ifNotExists);
- if (!shouldAdd) {
+ if (validatePartition(part, catName, tblName, dbName, partsToAdd, ms, ifNotExists,
+ partitionKeys)) {
+ partitionsToAdd.add(part);
+ } else {
existingParts.add(part);
- LOG.info("Not adding partition {} as it already exists", part);
- continue;
- }
-
- if (!partsToAdd.add(new PartValEqWrapperLite(part))) {
- // Technically, for ifNotExists case, we could insert one and discard the other
- // because the first one now "exists", but it seems better to report the problem
- // upstream as such a command doesn't make sense.
- throw new MetaException("Duplicate partitions in the list: " + part);
- }
-
- partitionsToAdd.add(part);
- }
-
- final AtomicBoolean failureOccurred = new AtomicBoolean(false);
- final Table table = tbl;
- List<Future<Partition>> partFutures = new ArrayList<>(partitionsToAdd.size());
-
- final UserGroupInformation ugi;
- try {
- ugi = UserGroupInformation.getCurrentUser();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- for (final Partition partition : partitionsToAdd) {
- initializePartitionParameters(table, partition);
-
- partFutures.add(threadPool.submit(() -> {
- if (failureOccurred.get()) {
- return null;
- }
- ugi.doAs((PrivilegedExceptionAction<Partition>) () -> {
- try {
- boolean madeDir = createLocationForAddedPartition(table, partition);
- addedPartitions.put(new PartValEqWrapperLite(partition), madeDir);
- initializeAddedPartition(table, partition, madeDir);
- } catch (MetaException e) {
- throw new IOException(e.getMessage(), e);
- }
- return null;
- });
- return partition;
- }));
- }
-
- String errorMessage = null;
- for (Future<Partition> partFuture : partFutures) {
- try {
- Partition part = partFuture.get();
- if (part != null && !failureOccurred.get()) {
- newParts.add(part);
- }
- } catch (InterruptedException | ExecutionException e) {
- // If an exception is thrown in the execution of a task, set the failureOccurred flag to
- // true. This flag is visible in the tasks and if its value is true, the partition
- // folders won't be created.
- // Then iterate through the remaining tasks and wait for them to finish. The tasks which
- // are started before the flag got set will then finish creating the partition folders.
- // The tasks which are started after the flag got set, won't create the partition
- // folders, to avoid unnecessary work.
- // This way it is sure that all tasks are finished, when entering the finally part where
- // the partition folders are cleaned up. It won't happen that a task is still running
- // when cleaning up the folders, so it is sure we won't have leftover folders.
- // Canceling the other tasks would be also an option but during testing it turned out
- // that it is not a trustworthy solution to avoid leftover folders.
- failureOccurred.compareAndSet(false, true);
- errorMessage = e.getMessage();
}
}
- if (failureOccurred.get()) {
- throw new MetaException(errorMessage);
- }
+ newParts.addAll(createPartitionFolders(partitionsToAdd, tbl, addedPartitions));
if (!newParts.isEmpty()) {
ms.addPartitions(catName, dbName, tblName, newParts);
@@ -3404,12 +3323,7 @@ public class HiveMetaStore extends ThriftHiveMetastore {
} finally {
if (!success) {
ms.rollbackTransaction();
- for (Map.Entry<PartValEqWrapperLite, Boolean> e : addedPartitions.entrySet()) {
- if (e.getValue()) {
- // we just created this directory - it's not a case of pre-creation, so we nuke.
- wh.deleteDir(new Path(e.getKey().location), true, db);
- }
- }
+ cleanupPartitionFolders(addedPartitions, db);
if (!listeners.isEmpty()) {
MetaStoreListenerNotifier.notifyEvent(listeners,
@@ -3438,6 +3352,177 @@ public class HiveMetaStore extends ThriftHiveMetastore {
return newParts;
}
+ /**
+ * Remove the newly created partition folders. The values in the addedPartitions map indicates
+ * whether or not the location of the partition was newly created. If the value is false, the
+ * partition folder will not be removed.
+ * @param addedPartitions
+ * @throws MetaException
+ * @throws IllegalArgumentException
+ */
+ private void cleanupPartitionFolders(final Map<PartValEqWrapperLite, Boolean> addedPartitions,
+ Database db) throws MetaException, IllegalArgumentException {
+ for (Map.Entry<PartValEqWrapperLite, Boolean> e : addedPartitions.entrySet()) {
+ if (e.getValue()) {
+ // we just created this directory - it's not a case of pre-creation, so we nuke.
+ wh.deleteDir(new Path(e.getKey().location), true, db);
+ }
+ }
+ }
+
+ /**
+ * Validate a partition before creating it. The validation checks
+ * <ul>
+ * <li>if the database and table names set in the partition are not null and they are matching
+ * with the expected values set in the tblName and dbName parameters.</li>
+ * <li>if the partition values are set.</li>
+ * <li>if none of the partition values is null.</li>
+ * <li>if the partition values are matching with the pattern set in the
+ * 'metastore.partition.name.whitelist.pattern' configuration property.</li>
+ * <li>if the partition doesn't already exist. If the partition already exists, an exception
+ * will be thrown if the ifNotExists parameter is false, otherwise it will be just ignored.</li>
+ * <li>if the partsToAdd set doesn't contain the partition. The partsToAdd set contains the
+ * partitions which are already validated. If the set contains the current partition, it means
+ * that the partition is tried to be added multiple times in the same batch. Please note that
+ * the set will be updated with the current partition if the validation was successful.</li>
+ * </ul>
+ * @param part
+ * @param catName
+ * @param tblName
+ * @param dbName
+ * @param partsToAdd
+ * @param ms
+ * @param ifNotExists
+ * @return
+ * @throws MetaException
+ * @throws TException
+ */
+ private boolean validatePartition(final Partition part, final String catName,
+ final String tblName, final String dbName, final Set<PartValEqWrapperLite> partsToAdd,
+ final RawStore ms, final boolean ifNotExists, List<FieldSchema> partitionKeys) throws MetaException, TException {
+
+ if (part.getDbName() == null || part.getTableName() == null) {
+ throw new MetaException("The database and table name must be set in the partition.");
+ }
+
+ if (!part.getTableName().equalsIgnoreCase(tblName)
+ || !part.getDbName().equalsIgnoreCase(dbName)) {
+ String errorMsg = String.format(
+ "Partition does not belong to target table %s. It belongs to the table %s.%s : %s",
+ TableName.getQualified(catName, dbName, tblName), part.getDbName(),
+ part.getTableName(), part.toString());
+ throw new MetaException(errorMsg);
+ }
+
+ if (part.getValues() == null || part.getValues().isEmpty()) {
+ throw new MetaException("The partition values cannot be null or empty.");
+ }
+
+ if (part.getValues().contains(null)) {
+ throw new MetaException("Partition value cannot be null.");
+ }
+
+ boolean shouldAdd = startAddPartition(ms, part, partitionKeys, ifNotExists);
+ if (!shouldAdd) {
+ LOG.info("Not adding partition {} as it already exists", part);
+ return false;
+ }
+
+ if (!partsToAdd.add(new PartValEqWrapperLite(part))) {
+ // Technically, for ifNotExists case, we could insert one and discard the other
+ // because the first one now "exists", but it seems better to report the problem
+ // upstream as such a command doesn't make sense.
+ throw new MetaException("Duplicate partitions in the list: " + part);
+ }
+ return true;
+ }
+
+ /**
+ * Create the location folders for the partitions. For each partition a separate thread will be
+ * started to create the folder. The method will wait until all threads are finished and returns
+ * the partitions whose folders were created successfully. If an error occurs during the
+ * execution of a thread, a MetaException will be thrown.
+ * @param partitionsToAdd
+ * @param table
+ * @param addedPartitions
+ * @return
+ * @throws MetaException
+ */
+ private List<Partition> createPartitionFolders(final List<Partition> partitionsToAdd,
+ final Table table, final Map<PartValEqWrapperLite, Boolean> addedPartitions)
+ throws MetaException {
+
+ final AtomicBoolean failureOccurred = new AtomicBoolean(false);
+ final List<Future<Partition>> partFutures = new ArrayList<>(partitionsToAdd.size());
+ final Map<PartValEqWrapperLite, Boolean> addedParts = new ConcurrentHashMap<>();
+
+ final UserGroupInformation ugi;
+ try {
+ ugi = UserGroupInformation.getCurrentUser();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ for (final Partition partition : partitionsToAdd) {
+ initializePartitionParameters(table, partition);
+
+ partFutures.add(threadPool.submit(() -> {
+ if (failureOccurred.get()) {
+ return null;
+ }
+ ugi.doAs((PrivilegedExceptionAction<Partition>) () -> {
+ try {
+ boolean madeDir = createLocationForAddedPartition(table, partition);
+ addedParts.put(new PartValEqWrapperLite(partition), madeDir);
+ initializeAddedPartition(table, partition, madeDir);
+ } catch (MetaException e) {
+ throw new IOException(e.getMessage(), e);
+ }
+ return null;
+ });
+ return partition;
+ }));
+ }
+
+ List<Partition> newParts = new ArrayList<>(partitionsToAdd.size());
+ String errorMessage = null;
+ for (Future<Partition> partFuture : partFutures) {
+ try {
+ Partition part = partFuture.get();
+ if (part != null && !failureOccurred.get()) {
+ newParts.add(part);
+ }
+ } catch (ExecutionException e) {
+ // If an exception is thrown in the execution of a task, set the failureOccurred flag to
+ // true. This flag is visible in the tasks and if its value is true, the partition
+ // folders won't be created.
+ // Then iterate through the remaining tasks and wait for them to finish. The tasks which
+ // are started before the flag got set will then finish creating the partition folders.
+ // The tasks which are started after the flag got set, won't create the partition
+ // folders, to avoid unnecessary work.
+ // This way it is sure that all tasks are finished, when entering the finally part where
+ // the partition folders are cleaned up. It won't happen that a task is still running
+ // when cleaning up the folders, so it is sure we won't have leftover folders.
+ // Canceling the other tasks would be also an option but during testing it turned out
+ // that it is not a trustworthy solution to avoid leftover folders.
+ failureOccurred.compareAndSet(false, true);
+ errorMessage = e.getMessage();
+ } catch (InterruptedException e) {
+ failureOccurred.compareAndSet(false, true);
+ errorMessage = e.getMessage();
+ // Restore interruption status of the corresponding thread
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ addedPartitions.putAll(addedParts);
+ if (failureOccurred.get()) {
+ throw new MetaException(errorMessage);
+ }
+
+ return newParts;
+ }
+
@Override
public AddPartitionsResult add_partitions_req(AddPartitionsRequest request)
throws TException {
@@ -3555,100 +3640,21 @@ public class HiveMetaStore extends ThriftHiveMetastore {
firePreEvent(new PreAddPartitionEvent(tbl, partitionSpecProxy, this));
Set<PartValEqWrapperLite> partsToAdd = new HashSet<>(partitionSpecProxy.size());
List<Partition> partitionsToAdd = new ArrayList<>(partitionSpecProxy.size());
+ List<FieldSchema> partitionKeys = tbl.getPartitionKeys();
while (partitionIterator.hasNext()) {
// Iterate through the partitions and validate them. If one of the partitions is
// incorrect, an exception will be thrown before the threads which create the partition
// folders are submitted. This way we can be sure that no partition or partition folder
// will be created if the list contains an invalid partition.
final Partition part = partitionIterator.getCurrent();
-
- if (part.getDbName() == null || part.getTableName() == null) {
- throw new MetaException("The database and table name must be set in the partition.");
- }
- if (!part.getTableName().equalsIgnoreCase(tblName) || !part.getDbName().equalsIgnoreCase(dbName)) {
- String errorMsg = String.format(
- "Partition does not belong to target table %s.%s. It belongs to the table %s.%s : %s",
- dbName, tblName, part.getDbName(), part.getTableName(), part.toString());
- throw new MetaException(errorMsg);
- }
- if (part.getValues() == null || part.getValues().isEmpty()) {
- throw new MetaException("The partition values cannot be null or empty.");
- }
-
- boolean shouldAdd = startAddPartition(ms, part, tbl.getPartitionKeys(), ifNotExists);
- if (!shouldAdd) {
- LOG.info("Not adding partition {} as it already exists", part);
- continue;
+ if (validatePartition(part, catName, tblName, dbName, partsToAdd, ms, ifNotExists,
+ partitionKeys)) {
+ partitionsToAdd.add(part);
}
-
- if (!partsToAdd.add(new PartValEqWrapperLite(part))) {
- // Technically, for ifNotExists case, we could insert one and discard the other
- // because the first one now "exists", but it seems better to report the problem
- // upstream as such a command doesn't make sense.
- throw new MetaException("Duplicate partitions in the list: " + part);
- }
-
- partitionsToAdd.add(part);
partitionIterator.next();
}
- final AtomicBoolean failureOccurred = new AtomicBoolean(false);
- List<Future<Partition>> partFutures = new ArrayList<>(partitionsToAdd.size());
- final Table table = tbl;
-
- final UserGroupInformation ugi;
- try {
- ugi = UserGroupInformation.getCurrentUser();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- for (final Partition partition : partitionsToAdd) {
- initializePartitionParameters(table, partition);
-
- partFutures.add(threadPool.submit(() -> {
- if (failureOccurred.get()) {
- return null;
- }
- ugi.doAs((PrivilegedExceptionAction<Partition>) () -> {
- try {
- boolean madeDir = createLocationForAddedPartition(table, partition);
- addedPartitions.put(new PartValEqWrapperLite(partition), madeDir);
- initializeAddedPartition(table, partition, madeDir);
- } catch (MetaException e) {
- throw new IOException(e.getMessage(), e);
- }
- return null;
- });
- return partition;
- }));
- }
-
- String errorMessage = null;
- for (Future<Partition> partFuture : partFutures) {
- try {
- partFuture.get();
- } catch (InterruptedException | ExecutionException e) {
- // If an exception is thrown in the execution of a task, set the failureOccurred flag to
- // true. This flag is visible in the tasks and if its value is true, the partition
- // folders won't be created.
- // Then iterate through the remaining tasks and wait for them to finish. The tasks which
- // are started before the flag got set will then finish creating the partition folders.
- // The tasks which are started after the flag got set, won't create the partition
- // folders, to avoid unnecessary work.
- // This way it is sure that all tasks are finished, when entering the finally part where
- // the partition folders are cleaned up. It won't happen that a task is still running
- // when cleaning up the folders, so it is sure we won't have leftover folders.
- // Canceling the other tasks would be also an option but during testing it turned out
- // that it is not a trustworthy solution to avoid leftover folders.
- failureOccurred.compareAndSet(false, true);
- errorMessage = e.getMessage();
- }
- }
-
- if (failureOccurred.get()) {
- throw new MetaException(errorMessage);
- }
+ createPartitionFolders(partitionsToAdd, tbl, addedPartitions);
ms.addPartitions(catName, dbName, tblName, partitionSpecProxy, ifNotExists);
@@ -3664,12 +3670,7 @@ public class HiveMetaStore extends ThriftHiveMetastore {
} finally {
if (!success) {
ms.rollbackTransaction();
- for (Map.Entry<PartValEqWrapperLite, Boolean> e : addedPartitions.entrySet()) {
- if (e.getValue()) {
- // we just created this directory - it's not a case of pre-creation, so we nuke.
- wh.deleteDir(new Path(e.getKey().location), true, db);
- }
- }
+ cleanupPartitionFolders(addedPartitions, db);
}
if (!listeners.isEmpty()) {
http://git-wip-us.apache.org/repos/asf/hive/blob/49abcf76/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitions.java
----------------------------------------------------------------------
diff --git a/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitions.java b/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitions.java
index bf559b4..a15f5ea 100644
--- a/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitions.java
+++ b/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitions.java
@@ -243,6 +243,33 @@ public class TestAddPartitions extends MetaStoreClientTest {
Assert.assertTrue(metaStore.isPathExists(new Path(part.getSd().getLocation())));
}
+ @Test
+ public void testAddPartitionUpperCaseDBAndTableName() throws Exception {
+
+ // Create table 'test_partition_db.test_add_part_table'
+ String tableName = "test_add_part_table";
+ String tableLocation = metaStore.getWarehouseRoot() + "/" + tableName.toUpperCase();
+ createTable(DB_NAME, tableName, getYearPartCol(), tableLocation);
+
+ // Create partition with table name 'TEST_ADD_PART_TABLE' and db name 'TEST_PARTITION_DB'
+ Partition partition = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(),
+ "2013", tableLocation + "/year=2013");
+ client.add_partition(partition);
+
+ // Validate the partition attributes
+ // The db and table name should be all lower case: 'test_partition_db' and
+ // 'test_add_part_table'
+ // The location should be saved case-sensitive, it should be
+ // warehouse dir + "TEST_ADD_PART_TABLE/year=2017"
+ Partition part = client.getPartition(DB_NAME, tableName, "year=2013");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2013", part.getSd().getLocation());
+ Partition part1 = client.getPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "year=2013");
+ Assert.assertEquals(part, part1);
+ }
+
@Test(expected = InvalidObjectException.class)
public void testAddPartitionNonExistingDb() throws Exception {
@@ -691,6 +718,65 @@ public class TestAddPartitions extends MetaStoreClientTest {
verifyPartitionAttributesDefaultValues(part, table.getSd().getLocation());
}
+ @Test
+ public void testAddPartitionsUpperCaseDBAndTableName() throws Exception {
+
+ // Create table 'test_partition_db.test_add_part_table'
+ String tableName = "test_add_part_table";
+ String tableLocation = metaStore.getWarehouseRoot() + "/" + tableName.toUpperCase();
+ createTable(DB_NAME, tableName, getYearPartCol(), tableLocation);
+
+ // Create partitions with table name 'TEST_ADD_PART_TABLE' and db name 'TEST_PARTITION_DB'
+ Partition partition1 = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "2017",
+ tableLocation + "/year=2017");
+ Partition partition2 = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "2018",
+ tableLocation + "/year=2018");
+ client.add_partitions(Lists.newArrayList(partition1, partition2));
+
+ // Validate the partitions attributes
+ // The db and table name should be all lower case: 'test_partition_db' and
+ // 'test_add_part_table'
+ // The location should be saved case-sensitive, it should be
+ // warehouse dir + "TEST_ADD_PART_TABLE/year=2017" and
+ // warehouse dir + "TEST_ADD_PART_TABLE/year=2018"
+ Partition part = client.getPartition(DB_NAME, tableName, "year=2017");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2017", part.getSd().getLocation());
+ part = client.getPartition(DB_NAME, tableName, "year=2018");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2018", part.getSd().getLocation());
+ }
+
+ @Test
+ public void testAddPartitionsUpperCaseDBAndTableNameInOnePart() throws Exception {
+
+ // Create table 'test_partition_db.test_add_part_table'
+ String tableName = "test_add_part_table";
+ String tableLocation = metaStore.getWarehouseRoot() + "/" + tableName.toUpperCase();
+ createTable(DB_NAME, tableName, getYearPartCol(), tableLocation);
+
+ // Create two partitions with table name 'test_add_part_table' and db name 'test_partition_db'
+ // Create one partition with table name 'TEST_ADD_PART_TABLE' and db name 'TEST_PARTITION_DB'
+ Partition partition1 = buildPartition(DB_NAME, tableName, "2017", tableLocation + "/year=2017");
+ Partition partition2 = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "2018",
+ tableLocation + "/year=2018");
+ Partition partition3 = buildPartition(DB_NAME, tableName, "2019", tableLocation + "/year=2019");
+ try {
+ client.add_partitions(Lists.newArrayList(partition1, partition2, partition3));
+ Assert.fail("MetaException should have been thrown.");
+ } catch (MetaException e) {
+ // Expected exception
+ }
+
+ List<String> partitionNames = client.listPartitionNames(DB_NAME, tableName, MAX);
+ Assert.assertNotNull(partitionNames);
+ Assert.assertTrue(partitionNames.isEmpty());
+ }
+
@Test(expected = MetaException.class)
public void testAddPartitionsNullList() throws Exception {
http://git-wip-us.apache.org/repos/asf/hive/blob/49abcf76/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitionsFromPartSpec.java
----------------------------------------------------------------------
diff --git a/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitionsFromPartSpec.java b/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitionsFromPartSpec.java
index 4f11a55..2564349 100644
--- a/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitionsFromPartSpec.java
+++ b/standalone-metastore/src/test/java/org/apache/hadoop/hive/metastore/client/TestAddPartitionsFromPartSpec.java
@@ -147,6 +147,32 @@ public class TestAddPartitionsFromPartSpec extends MetaStoreClientTest {
}
@Test
+ public void testAddPartitionSpecWithSharedSDUpperCaseDBAndTableName() throws Exception {
+
+ Table table = createTable();
+ PartitionWithoutSD partition = buildPartitionWithoutSD(Lists.newArrayList("2013"), 1);
+ List<PartitionWithoutSD> partitions = Lists.newArrayList(partition);
+
+ String location = table.getSd().getLocation() + "/sharedSDTest/";
+ PartitionSpec partitionSpec = new PartitionSpec();
+ partitionSpec.setDbName(DB_NAME.toUpperCase());
+ partitionSpec.setTableName(TABLE_NAME.toUpperCase());
+ PartitionSpecWithSharedSD partitionList = new PartitionSpecWithSharedSD();
+ partitionList.setPartitions(partitions);
+ partitionList.setSd(buildSD(location));
+ partitionSpec.setSharedSDPartitionSpec(partitionList);
+ PartitionSpecProxy partitionSpecProxy = PartitionSpecProxy.Factory.get(partitionSpec);
+ client.add_partitions_pspec(partitionSpecProxy);
+
+ Partition part = client.getPartition(DB_NAME, TABLE_NAME, "year=2013");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(TABLE_NAME, part.getTableName());
+ Assert.assertEquals(metaStore.getWarehouseRoot() + "/" + TABLE_NAME +
+ "/sharedSDTest/partwithoutsd1", part.getSd().getLocation());
+ }
+
+ @Test
public void testAddPartitionSpecsMultipleValues() throws Exception {
Table table = createTable(DB_NAME, TABLE_NAME, getYearAndMonthPartCols(),
@@ -170,6 +196,76 @@ public class TestAddPartitionsFromPartSpec extends MetaStoreClientTest {
verifyPartitionSharedSD(table, "year=2005/month=may", Lists.newArrayList("2005", "may"), 4);
}
+ @Test
+ public void testAddPartitionSpecUpperCaseDBAndTableName() throws Exception {
+
+ // Create table 'test_partition_db.test_add_part_table'
+ String tableName = "test_add_part_table";
+ String tableLocation = metaStore.getWarehouseRoot() + "/" + tableName.toUpperCase();
+ createTable(DB_NAME, tableName, getYearPartCol(), tableLocation);
+
+ // Create partitions with table name 'TEST_ADD_PART_TABLE' and db name 'TEST_PARTITION_DB'
+ Partition partition1 = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "2013",
+ tableLocation + "/year=2013");
+ Partition partition2 = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "2014",
+ tableLocation + "/year=2014");
+ List<Partition> partitions = Lists.newArrayList(partition1, partition2);
+
+ String rootPath = tableLocation + "/addpartspectest/";
+ PartitionSpecProxy partitionSpec =
+ buildPartitionSpec(DB_NAME.toUpperCase(), tableName.toUpperCase(), rootPath, partitions);
+ client.add_partitions_pspec(partitionSpec);
+
+ // Validate the partition attributes
+ // The db and table name should be all lower case: 'test_partition_db' and
+ // 'test_add_part_table'
+ // The location should be saved case-sensitive, it should be
+ // warehouse dir + "TEST_ADD_PART_TABLE/year=2017"
+ Partition part = client.getPartition(DB_NAME, tableName, "year=2013");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2013", part.getSd().getLocation());
+ Partition part1 = client.getPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "year=2013");
+ Assert.assertEquals(part, part1);
+ part = client.getPartition(DB_NAME, tableName, "year=2014");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2014", part.getSd().getLocation());
+ part1 = client.getPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "year=2014");
+ Assert.assertEquals(part, part1);
+ }
+
+ @Test
+ public void testAddPartitionSpecUpperCaseDBAndTableNameInOnePart() throws Exception {
+
+ String tableName = "test_add_part_table";
+ String tableLocation = metaStore.getWarehouseRoot() + "/" + tableName.toUpperCase();
+ createTable(DB_NAME, tableName, getYearPartCol(), tableLocation);
+
+ Partition partition1 = buildPartition(DB_NAME, tableName, "2013",
+ tableLocation + "/year=2013");
+ Partition partition2 = buildPartition(DB_NAME.toUpperCase(), tableName.toUpperCase(), "2014",
+ tableLocation + "/year=2014");
+ List<Partition> partitions = Lists.newArrayList(partition1, partition2);
+
+ String rootPath = tableLocation + "/addpartspectest/";
+ PartitionSpecProxy partitionSpec = buildPartitionSpec(DB_NAME, tableName, rootPath, partitions);
+ client.add_partitions_pspec(partitionSpec);
+
+ Partition part = client.getPartition(DB_NAME, tableName, "year=2013");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2013", part.getSd().getLocation());
+ part = client.getPartition(DB_NAME, tableName, "year=2014");
+ Assert.assertNotNull(part);
+ Assert.assertEquals(tableName, part.getTableName());
+ Assert.assertEquals(DB_NAME, part.getDbName());
+ Assert.assertEquals(tableLocation + "/year=2014", part.getSd().getLocation());
+ }
+
// TODO add tests for partitions in other catalogs
@Test(expected = MetaException.class)