@Override
  public List<String> getAllTables(final String dbName) throws HiveMetastoreException {
    ArrayList<String> tables = new ArrayList<>();

    for (HiveObjectSpec spec : specToTable.keySet()) {
      if (spec.getDbName().equals(dbName)) {
        tables.add(spec.getTableName());
      }
    }

    return tables;
  }
  @Override
  public List<String> getTables(String dbName, String tableName) throws HiveMetastoreException {
    if (!tableName.equals("*")) {
      throw new RuntimeException("Only * (wildcard) is supported in " + "the mock client");
    }
    List<String> tableNames = new ArrayList<>();

    for (HiveObjectSpec spec : specToTable.keySet()) {
      if (spec.getDbName().equals(dbName)) {
        tableNames.add(spec.getTableName());
      }
    }
    return tableNames;
  }
  @Override
  public void alterPartition(String dbName, String tableName, Partition partition)
      throws HiveMetastoreException {
    HiveObjectSpec tableSpec = new HiveObjectSpec(partition.getDbName(), partition.getTableName());
    if (!specToTable.containsKey(tableSpec)) {
      throw new HiveMetastoreException("Unknown table: " + tableSpec);
    }
    Table table = specToTable.get(tableSpec);
    String partitionName = getPartitionName(table, partition);

    HiveObjectSpec partitionSpec =
        new HiveObjectSpec(tableSpec.getDbName(), tableSpec.getTableName(), partitionName);
    if (!specToPartition.containsKey(partitionSpec)) {
      throw new HiveMetastoreException("Partition does not exist: " + partitionSpec);
    }

    specToPartition.put(partitionSpec, partition);
  }
  @Override
  public Partition addPartition(Partition partition) throws HiveMetastoreException {
    HiveObjectSpec tableSpec = new HiveObjectSpec(partition.getDbName(), partition.getTableName());
    if (!specToTable.containsKey(tableSpec)) {
      throw new HiveMetastoreException("Unknown table: " + tableSpec);
    }
    Table table = specToTable.get(tableSpec);
    String partitionName = getPartitionName(table, partition);

    HiveObjectSpec partitionSpec =
        new HiveObjectSpec(tableSpec.getDbName(), tableSpec.getTableName(), partitionName);

    if (specToPartition.containsKey(partitionSpec)) {
      throw new HiveMetastoreException("Partition already exists: " + partitionSpec);
    }

    specToPartition.put(partitionSpec, partition);
    return partition;
  }
  protected void reduce(LongWritable key, Iterable<Text> values, Context context)
      throws IOException, InterruptedException {

    for (Text value : values) {
      Pair<TaskEstimate, HiveObjectSpec> input =
          MetastoreReplicationJob.deseralizeJobResult(value.toString());
      TaskEstimate estimate = input.getLeft();
      HiveObjectSpec spec = input.getRight();
      String result = value.toString();
      String extra = "";

      try {
        if (estimate.getTaskType() == TaskEstimate.TaskType.CHECK_PARTITION) {
          // Table exists in source, but not in dest. It should copy the table.
          TaskEstimate newEstimate = estimator.analyze(spec);

          result = MetastoreReplicationJob.serializeJobResult(newEstimate, spec);
        }
      } catch (HiveMetastoreException e) {
        LOG.error(
            String.format(
                "Hit exception during db:%s, tbl:%s, part:%s",
                spec.getDbName(), spec.getTableName(), spec.getPartitionName()),
            e);
        extra =
            String.format(
                "exception in %s of mapper = %s",
                estimate.getTaskType().toString(), context.getTaskAttemptID().toString());
      }

      context.write(new Text(result), new Text(extra));
      ++this.count;
      if (this.count % 100 == 0) {
        LOG.info("Processed " + this.count + " entities");
      }
    }
  }