// Path: metacat-common-server/src/main/java/com/netflix/metacat/common/server/partition/util/PartitionUtil.java
// @Slf4j
// public final class PartitionUtil {
//     /** Default partition value. */
//     public static final String DEFAULT_PARTITION_NAME = "__HIVE_DEFAULT_PARTITION__";
//     private static final Splitter EQUAL_SPLITTER = Splitter.on('=').trimResults();
//     private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
// 
//     private PartitionUtil() {
//     }
// 
//     /**
//      * Returns the partition key values from the given path.
//      * @param location location path
//      * @return the partition key values from the given path.
//      */
//     public static Map<String, String> getPartitionKeyValues(final String location) {
//         final Map<String, String> parts = Maps.newLinkedHashMap();
//         getPartitionKeyValues(location, parts);
//         return parts;
//     }
// 
//     /**
//      * Sets the partition key values from the given path.
//      * @param location location path
//      * @param parts parts
//      */
//     public static void getPartitionKeyValues(final String location, final Map<String, String> parts) {
//         for (String part : Splitter.on('/').omitEmptyStrings().split(location)) {
//             if (part.contains("=")) {
//                 final String[] values = part.split("=", 2);
//                 //
//                 // Ignore the partition value, if it is null or the hive default. Hive sets the default value if the
//                 // value is null/empty string or any other values that cannot be escaped.
//                 //
//                 if (values[0].equalsIgnoreCase("null")
//                     || values[1].equalsIgnoreCase("null")) {
//                     log.debug("Found 'null' string in kvp [{}] skipping.", part);
//                 } else if (values[1].equalsIgnoreCase(DEFAULT_PARTITION_NAME)) {
//                     parts.put(values[0], null);
//                 } else {
//                     parts.put(values[0], values[1]);
//                 }
//             }
//         }
//     }
// 
//     /**
//      * Validates the partition name.
//      * @param partitionName partition name
//      * @param partitionKeys partition keys
//      */
//     public static void validatePartitionName(final String partitionName, final List<String> partitionKeys) {
//         if (partitionKeys == null || partitionKeys.isEmpty()) {
//             throw new IllegalStateException("No partitionKeys are defined");
//         }
// 
//         for (String part : SLASH_SPLITTER.split(partitionName)) {
//             final List<String> tokens = EQUAL_SPLITTER.splitToList(part);
//             if (tokens.size() != 2) {
//                 throw new IllegalArgumentException(String.format("Partition name '%s' is invalid", partitionName));
//             }
//             final String key = tokens.get(0);
//             final String value = tokens.get(1);
//             if (!partitionKeys.contains(key) || value.isEmpty() || "null".equalsIgnoreCase(value)) {
//                 throw new IllegalArgumentException(String.format("Partition name '%s' is invalid", partitionName));
//             }
//         }
//     }
// }


import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.netflix.metacat.common.server.connectors.exception.ConnectorException;
import com.netflix.metacat.common.server.partition.util.PartitionUtil;
import com.netflix.metacat.connector.hive.IMetacatHiveClient;
import com.netflix.metacat.connector.hive.metastore.IMetacatHMSHandler;
import com.netflix.metacat.connector.hive.monitoring.HiveMetrics;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.hive.metastore.api.Database;
import org.apache.hadoop.hive.metastore.api.MetaException;
import org.apache.hadoop.hive.metastore.api.Partition;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.thrift.TException;
import org.datanucleus.exceptions.NucleusDataStoreException;
import javax.annotation.Nullable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
        callWrap(HiveMetrics.TagAlterTable.getMetricName(), () -> {
            handler.alter_table(databaseName, tableName, table);
            return null;
        });
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void alterPartitions(final String dbName,
                                final String tableName,
                                final List<Partition> partitions) throws TException {
        callWrap(HiveMetrics.TagAlterPartitions.getMetricName(), () -> {
            handler.alter_partitions(dbName, tableName, partitions);
            return null;
        });
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void addDropPartitions(final String dbName,
                                  final String tableName,
                                  final List<Partition> addParts,
                                  final List<String> dropPartNames) throws TException {
        callWrap(HiveMetrics.TagAddDropPartitions.getMetricName(), () -> {
            final List<List<String>> dropParts = new ArrayList<>();
            for (String partName : dropPartNames) {
// Path: metacat-common-server/src/main/java/com/netflix/metacat/common/server/partition/util/PartitionUtil.java
// @Slf4j
// public final class PartitionUtil {
//     /** Default partition value. */
//     public static final String DEFAULT_PARTITION_NAME = "__HIVE_DEFAULT_PARTITION__";
//     private static final Splitter EQUAL_SPLITTER = Splitter.on('=').trimResults();
//     private static final Splitter SLASH_SPLITTER = Splitter.on('/').omitEmptyStrings().trimResults();
// 
//     private PartitionUtil() {
//     }
// 
//     /**
//      * Returns the partition key values from the given path.
//      * @param location location path
//      * @return the partition key values from the given path.
//      */
//     public static Map<String, String> getPartitionKeyValues(final String location) {
//         final Map<String, String> parts = Maps.newLinkedHashMap();
//         getPartitionKeyValues(location, parts);
//         return parts;
//     }
// 
//     /**
//      * Sets the partition key values from the given path.
//      * @param location location path
//      * @param parts parts
//      */
//     public static void getPartitionKeyValues(final String location, final Map<String, String> parts) {
//         for (String part : Splitter.on('/').omitEmptyStrings().split(location)) {
//             if (part.contains("=")) {
//                 final String[] values = part.split("=", 2);
//                 //
//                 // Ignore the partition value, if it is null or the hive default. Hive sets the default value if the
//                 // value is null/empty string or any other values that cannot be escaped.
//                 //
//                 if (values[0].equalsIgnoreCase("null")
//                     || values[1].equalsIgnoreCase("null")) {
//                     log.debug("Found 'null' string in kvp [{}] skipping.", part);
//                 } else if (values[1].equalsIgnoreCase(DEFAULT_PARTITION_NAME)) {
//                     parts.put(values[0], null);
//                 } else {
//                     parts.put(values[0], values[1]);
//                 }
//             }
//         }
//     }
// 
//     /**
//      * Validates the partition name.
//      * @param partitionName partition name
//      * @param partitionKeys partition keys
//      */
//     public static void validatePartitionName(final String partitionName, final List<String> partitionKeys) {
//         if (partitionKeys == null || partitionKeys.isEmpty()) {
//             throw new IllegalStateException("No partitionKeys are defined");
//         }
// 
//         for (String part : SLASH_SPLITTER.split(partitionName)) {
//             final List<String> tokens = EQUAL_SPLITTER.splitToList(part);
//             if (tokens.size() != 2) {
//                 throw new IllegalArgumentException(String.format("Partition name '%s' is invalid", partitionName));
//             }
//             final String key = tokens.get(0);
//             final String value = tokens.get(1);
//             if (!partitionKeys.contains(key) || value.isEmpty() || "null".equalsIgnoreCase(value)) {
//                 throw new IllegalArgumentException(String.format("Partition name '%s' is invalid", partitionName));
//             }
//         }
//     }
// }


// Path: metacat-connector-hive/src/main/java/com/netflix/metacat/connector/hive/client/embedded/EmbeddedHiveClient.java
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.netflix.metacat.common.server.connectors.exception.ConnectorException;
import com.netflix.metacat.common.server.partition.util.PartitionUtil;
import com.netflix.metacat.connector.hive.IMetacatHiveClient;
import com.netflix.metacat.connector.hive.metastore.IMetacatHMSHandler;
import com.netflix.metacat.connector.hive.monitoring.HiveMetrics;
import com.netflix.spectator.api.Counter;
import com.netflix.spectator.api.Id;
import com.netflix.spectator.api.Registry;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.hive.metastore.api.Database;
import org.apache.hadoop.hive.metastore.api.MetaException;
import org.apache.hadoop.hive.metastore.api.Partition;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.thrift.TException;
import org.datanucleus.exceptions.NucleusDataStoreException;
import javax.annotation.Nullable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

        callWrap(HiveMetrics.TagAlterTable.getMetricName(), () -> {
            handler.alter_table(databaseName, tableName, table);
            return null;
        });
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void alterPartitions(final String dbName,
                                final String tableName,
                                final List<Partition> partitions) throws TException {
        callWrap(HiveMetrics.TagAlterPartitions.getMetricName(), () -> {
            handler.alter_partitions(dbName, tableName, partitions);
            return null;
        });
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void addDropPartitions(final String dbName,
                                  final String tableName,
                                  final List<Partition> addParts,
                                  final List<String> dropPartNames) throws TException {
        callWrap(HiveMetrics.TagAddDropPartitions.getMetricName(), () -> {
            final List<List<String>> dropParts = new ArrayList<>();
            for (String partName : dropPartNames) {
                dropParts.add(new ArrayList<>(PartitionUtil.getPartitionKeyValues(partName).values()));