<commit_before>/*
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed 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.hbase.client;


import io.grpc.Status;
import io.grpc.Status.OperationRuntimeException;

import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ClusterStatus;
import org.apache.hadoop.hbase.HBaseIOException;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableExistsException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotEnabledException;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;

import com.google.bigtable.admin.table.v1.ColumnFamily;
import com.google.bigtable.admin.table.v1.CreateColumnFamilyRequest;
import com.google.bigtable.admin.table.v1.CreateTableRequest;
import com.google.bigtable.admin.table.v1.DeleteColumnFamilyRequest;
import com.google.bigtable.admin.table.v1.DeleteTableRequest;
import com.google.bigtable.admin.table.v1.DeleteTableRequest.Builder;
import com.google.bigtable.admin.table.v1.GetTableRequest;
import com.google.bigtable.admin.table.v1.ListTablesRequest;
import com.google.bigtable.admin.table.v1.ListTablesResponse;
import com.google.bigtable.admin.table.v1.Table;
import com.google.cloud.bigtable.hbase.BigtableOptions;
import com.google.cloud.bigtable.hbase.Logger;
import com.google.cloud.bigtable.hbase.adapters.ClusterMetadataSetter;
import com.google.cloud.bigtable.hbase.adapters.ColumnDescriptorAdapter;
import com.google.cloud.bigtable.hbase.adapters.ColumnFamilyFormatter;
import com.google.cloud.bigtable.hbase.adapters.TableAdapter;
import com.google.cloud.bigtable.hbase.adapters.TableMetadataSetter;
import com.google.cloud.bigtable.grpc.BigtableAdminClient;
import com.google.common.base.MoreObjects;
import com.google.common.util.concurrent.UncheckedExecutionException;

public class BigtableAdmin implements Admin {

  private static final Logger LOG = new Logger(BigtableAdmin.class);

  /**
   * Bigtable doesn't require disabling tables before deletes or schema changes. Some clients do
   * call disable first, and then check for disable before deletes or schema changes. We're keeping
   * track of that state in memory on so that those clients can proceed with the delete/schema
   * change
   */
  private final Set<TableName> disabledTables;

  private final Configuration configuration;
  private final BigtableOptions options;
  private final BigtableConnection connection;
  private final BigtableAdminClient bigtableAdminClient;

  private ClusterMetadataSetter clusterMetadataSetter;
  private final ColumnDescriptorAdapter columnDescriptorAdapter = new ColumnDescriptorAdapter();
  private final TableAdapter tableAdapter;

  public BigtableAdmin(
      BigtableOptions options,
      Configuration configuration,
      BigtableConnection connection,
      BigtableAdminClient bigtableAdminClient,
      Set<TableName> disabledTables) {
    LOG.debug("Creating BigtableAdmin");
    this.configuration = configuration;
    this.options = options;
    this.connection = connection;
    this.bigtableAdminClient = bigtableAdminClient;
    this.disabledTables = disabledTables;
    this.clusterMetadataSetter = ClusterMetadataSetter.from(options);
    this.tableAdapter = new TableAdapter(options, columnDescriptorAdapter);
  }

  @Override
  public int getOperationTimeout() {
    throw new UnsupportedOperationException();  // TODO
  }

  @Override
  public void abort(String why, Throwable e) {
    throw new UnsupportedOperationException();  // TODO
  }

  @Override
  public boolean isAborted() {
    throw new UnsupportedOperationException();  // TODO
  }

  @Override
  public Connection getConnection() {
    return connection;
  }

  @Override
  public boolean tableExists(TableName tableName) throws IOException {
    for(TableName existingTableName : listTableNames(tableName.getNameAsString())) {
      if (existingTableName.equals(tableName)) {
        return true;
      }
    }
    return false;
  }

  // Used by the Hbase shell but not defined by Admin. Will be removed once the
  // shell is switch to use the methods defined in the interface.
  @Deprecated
  public boolean tableExists(String tableName) throws IOException {
    return tableExists(TableName.valueOf(tableName));
  }

  @Override
  public HTableDescriptor[] listTables() throws IOException {
    // NOTE: We don't have systables.
    return getTableDescriptors(listTableNames());
  }

  private HTableDescriptor[] getTableDescriptors(TableName[] tableNames) throws IOException {
    HTableDescriptor[] response = new HTableDescriptor[tableNames.length];
    for (int i = 0; i < tableNames.length; i++) {
      response[i] = getTableDescriptor(tableNames[i]);
    }
    return response;
  }

  @Override
  public HTableDescriptor[] listTables(Pattern pattern) throws IOException {
    // NOTE: We don't have systables.
    return getTableDescriptors(listTableNames(pattern));
  }

  @Override
  public HTableDescriptor[] listTables(final Pattern pattern, final boolean includeSysTables)
      throws IOException {
    return listTables(pattern);
  }


  // Used by the Hbase shell but not defined by Admin. Will be removed once the
  // shell is switch to use the methods defined in the interface.
  @Deprecated
  public TableName[] listTableNames(String patternStr) throws IOException {
    return listTableNames(Pattern.compile(patternStr));
  }

  @Override
  public TableName[] listTableNames(Pattern pattern) throws IOException {
    List<TableName> result = new ArrayList<>();

    for (TableName tableName : listTableNames()) {
      if (pattern.matcher(tableName.getNameAsString()).matches()) {
        result.add(tableName);
      }
    }

    return result.toArray(new TableName[result.size()]);
  }

  @Override
  public TableName[] listTableNames(Pattern pattern, boolean includeSysTables) throws IOException {
    return listTableNames(pattern);
  }

  @Override
  public TableName[] listTableNames(String regex, boolean includeSysTables) throws IOException {
    return listTableNames(regex);
  }
  @Override
  public HTableDescriptor[] listTables(String regex) throws IOException {
    return listTables(Pattern.compile(regex));
  }

  @Override
  public HTableDescriptor[] listTables(String regex, boolean includeSysTables) throws IOException {
    return listTables(regex);
  }

  @Override
  /**
   * Lists all table names for the cluster provided in the configuration.
   */
  public TableName[] listTableNames() throws IOException {
    return asTableNames(requestTableList().getTablesList());
  }

  /**
   * Request a list of Tables for the cluster.  The {@link Table}s in the response will only
   * contain fully qualified Bigtable table names, and not column family information.
   */
  private ListTablesResponse requestTableList() throws IOException {
    try {
      ListTablesRequest.Builder builder = ListTablesRequest.newBuilder();
      clusterMetadataSetter.setMetadata(builder);
      return bigtableAdminClient.listTables(builder.build());
    } catch (Throwable throwable) {
      throw new IOException("Failed to listTables", throwable);
    }
  }

  /**
   * Convert a list of Bigtable {@link Table}s to hbase {@link TableName}.
   */
  private TableName[] asTableNames(List<Table> tablesList) {
    TableName[] result = new TableName[tablesList.size()];
    for (int i = 0; i < tablesList.size(); i++) {
      // This will contain things like project, zone and cluster.
      String bigtableFullTableName = tablesList.get(i).getName();

      // Strip out the Bigtable info.
      String name = clusterMetadataSetter.toHBaseTableName(bigtableFullTableName);

      result[i] = TableName.valueOf(name);
    }
    return result;
  }

  @Override
  public HTableDescriptor getTableDescriptor(TableName tableName)
      throws TableNotFoundException, IOException {
    if (tableName == null) {
      return null;
    }

    String bigtableTableName = TableMetadataSetter.getBigtableName(tableName, options);
    GetTableRequest request = GetTableRequest.newBuilder().setName(bigtableTableName).build();

    try {
      return tableAdapter.adapt(bigtableAdminClient.getTable(request));
    } catch (UncheckedExecutionException e) {
      if (e.getCause() != null && e.getCause() instanceof OperationRuntimeException) {
        Status status = ((OperationRuntimeException) e.getCause()).getStatus();
        if (status.getCode() == Status.NOT_FOUND.getCode()) {
          throw new TableNotFoundException(tableName);
        }
      }
      throw new IOException("Failed to getTableDescriptor() on " + tableName, e);
    } catch (Throwable throwable) {
      throw new IOException("Failed to getTableDescriptor() on " + tableName, throwable);
    }
  }

  // Used by the Hbase shell but not defined by Admin. Will be removed once the
  // shell is switch to use the methods defined in the interface.
  @Deprecated
  public String[] getTableNames(String regex) throws IOException {
    HTableDescriptor[] tableDescriptors = listTables(regex);
    String[] tableNames = new String[tableDescriptors.length];
    for (int i = 0; i < tableDescriptors.length; i++) {
      tableNames[i] = tableDescriptors[i].getNameAsString();
    }
    return tableNames;
  }

  @Override
  public void createTable(HTableDescriptor desc) throws IOException {
    CreateTableRequest.Builder builder = CreateTableRequest.newBuilder();
    clusterMetadataSetter.setMetadata(builder);
    builder.setTableId(desc.getTableName().getQualifierAsString());
    builder.setTable(tableAdapter.adapt(desc));
    try {
      bigtableAdminClient.createTable(builder.build());
    } catch (Throwable throwable) {
      throw new IOException(
          String.format(
              "Failed to create table '%s'",
              desc.getTableName().getNameAsString()),
          throwable);
    }
  }

  @Override
  public void createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions)
      throws IOException {
    LOG.warn("Creating table, but ignoring parameters startKey, endKey and numRegions");
    createTable(desc);
  }

  @Override
  public void createTable(HTableDescriptor desc, byte[][] splitKeys) throws IOException {
    LOG.warn("Creating table, but ignoring parameters splitKeys");
    createTable(desc);
  }

  @Override
  public void createTableAsync(HTableDescriptor desc, byte[][] splitKeys) throws IOException {
    LOG.warn("Creating the table synchronously");
    createTable(desc);
  }

  @Override
  public void deleteTable(TableName tableName) throws IOException {
    Builder deleteBuilder = DeleteTableRequest.newBuilder();
    TableMetadataSetter.from(tableName, options).setMetadata(deleteBuilder);
    try {
      bigtableAdminClient.deleteTable(deleteBuilder.build());
    } catch (Throwable throwable) {
      throw new IOException(
          String.format(
              "Failed to delete table '%s'",
              tableName.getNameAsString()),
          throwable);
    }
  }

  @Override
  public HTableDescriptor[] deleteTables(String regex) throws IOException {
    return deleteTables(Pattern.compile(regex));
  }

  @Override
  public HTableDescriptor[] deleteTables(Pattern pattern) throws IOException {
    List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
    for (HTableDescriptor table : listTables(pattern)) {
      try {
        deleteTable(table.getTableName());
      } catch (IOException ex) {
        LOG.info("Failed to delete table " + table.getTableName(), ex);
        failed.add(table);
      }
    }
    return failed.toArray(new HTableDescriptor[failed.size()]);
  }

  @Override
  public void truncateTable(TableName tableName, boolean preserveSplits) throws IOException {
    throw new UnsupportedOperationException("truncateTable");  // TODO
  }

  @Override<commit_msg>Removing a table from the disabled list after it's deleted.<commit_after>/*
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed 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.hbase.client;


import io.grpc.Status;
import io.grpc.Status.OperationRuntimeException;

import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ClusterStatus;
import org.apache.hadoop.hbase.HBaseIOException;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.NamespaceDescriptor;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableExistsException;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.TableNotEnabledException;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;

import com.google.bigtable.admin.table.v1.ColumnFamily;
import com.google.bigtable.admin.table.v1.CreateColumnFamilyRequest;
import com.google.bigtable.admin.table.v1.CreateTableRequest;
import com.google.bigtable.admin.table.v1.DeleteColumnFamilyRequest;
import com.google.bigtable.admin.table.v1.DeleteTableRequest;
import com.google.bigtable.admin.table.v1.DeleteTableRequest.Builder;
import com.google.bigtable.admin.table.v1.GetTableRequest;
import com.google.bigtable.admin.table.v1.ListTablesRequest;
import com.google.bigtable.admin.table.v1.ListTablesResponse;
import com.google.bigtable.admin.table.v1.Table;
import com.google.cloud.bigtable.hbase.BigtableOptions;
import com.google.cloud.bigtable.hbase.Logger;
import com.google.cloud.bigtable.hbase.adapters.ClusterMetadataSetter;
import com.google.cloud.bigtable.hbase.adapters.ColumnDescriptorAdapter;
import com.google.cloud.bigtable.hbase.adapters.ColumnFamilyFormatter;
import com.google.cloud.bigtable.hbase.adapters.TableAdapter;
import com.google.cloud.bigtable.hbase.adapters.TableMetadataSetter;
import com.google.cloud.bigtable.grpc.BigtableAdminClient;
import com.google.common.base.MoreObjects;
import com.google.common.util.concurrent.UncheckedExecutionException;

public class BigtableAdmin implements Admin {

  private static final Logger LOG = new Logger(BigtableAdmin.class);

  /**
   * Bigtable doesn't require disabling tables before deletes or schema changes. Some clients do
   * call disable first, and then check for disable before deletes or schema changes. We're keeping
   * track of that state in memory on so that those clients can proceed with the delete/schema
   * change
   */
  private final Set<TableName> disabledTables;

  private final Configuration configuration;
  private final BigtableOptions options;
  private final BigtableConnection connection;
  private final BigtableAdminClient bigtableAdminClient;

  private ClusterMetadataSetter clusterMetadataSetter;
  private final ColumnDescriptorAdapter columnDescriptorAdapter = new ColumnDescriptorAdapter();
  private final TableAdapter tableAdapter;

  public BigtableAdmin(
      BigtableOptions options,
      Configuration configuration,
      BigtableConnection connection,
      BigtableAdminClient bigtableAdminClient,
      Set<TableName> disabledTables) {
    LOG.debug("Creating BigtableAdmin");
    this.configuration = configuration;
    this.options = options;
    this.connection = connection;
    this.bigtableAdminClient = bigtableAdminClient;
    this.disabledTables = disabledTables;
    this.clusterMetadataSetter = ClusterMetadataSetter.from(options);
    this.tableAdapter = new TableAdapter(options, columnDescriptorAdapter);
  }

  @Override
  public int getOperationTimeout() {
    throw new UnsupportedOperationException();  // TODO
  }

  @Override
  public void abort(String why, Throwable e) {
    throw new UnsupportedOperationException();  // TODO
  }

  @Override
  public boolean isAborted() {
    throw new UnsupportedOperationException();  // TODO
  }

  @Override
  public Connection getConnection() {
    return connection;
  }

  @Override
  public boolean tableExists(TableName tableName) throws IOException {
    for(TableName existingTableName : listTableNames(tableName.getNameAsString())) {
      if (existingTableName.equals(tableName)) {
        return true;
      }
    }
    return false;
  }

  // Used by the Hbase shell but not defined by Admin. Will be removed once the
  // shell is switch to use the methods defined in the interface.
  @Deprecated
  public boolean tableExists(String tableName) throws IOException {
    return tableExists(TableName.valueOf(tableName));
  }

  @Override
  public HTableDescriptor[] listTables() throws IOException {
    // NOTE: We don't have systables.
    return getTableDescriptors(listTableNames());
  }

  private HTableDescriptor[] getTableDescriptors(TableName[] tableNames) throws IOException {
    HTableDescriptor[] response = new HTableDescriptor[tableNames.length];
    for (int i = 0; i < tableNames.length; i++) {
      response[i] = getTableDescriptor(tableNames[i]);
    }
    return response;
  }

  @Override
  public HTableDescriptor[] listTables(Pattern pattern) throws IOException {
    // NOTE: We don't have systables.
    return getTableDescriptors(listTableNames(pattern));
  }

  @Override
  public HTableDescriptor[] listTables(final Pattern pattern, final boolean includeSysTables)
      throws IOException {
    return listTables(pattern);
  }


  // Used by the Hbase shell but not defined by Admin. Will be removed once the
  // shell is switch to use the methods defined in the interface.
  @Deprecated
  public TableName[] listTableNames(String patternStr) throws IOException {
    return listTableNames(Pattern.compile(patternStr));
  }

  @Override
  public TableName[] listTableNames(Pattern pattern) throws IOException {
    List<TableName> result = new ArrayList<>();

    for (TableName tableName : listTableNames()) {
      if (pattern.matcher(tableName.getNameAsString()).matches()) {
        result.add(tableName);
      }
    }

    return result.toArray(new TableName[result.size()]);
  }

  @Override
  public TableName[] listTableNames(Pattern pattern, boolean includeSysTables) throws IOException {
    return listTableNames(pattern);
  }

  @Override
  public TableName[] listTableNames(String regex, boolean includeSysTables) throws IOException {
    return listTableNames(regex);
  }
  @Override
  public HTableDescriptor[] listTables(String regex) throws IOException {
    return listTables(Pattern.compile(regex));
  }

  @Override
  public HTableDescriptor[] listTables(String regex, boolean includeSysTables) throws IOException {
    return listTables(regex);
  }

  @Override
  /**
   * Lists all table names for the cluster provided in the configuration.
   */
  public TableName[] listTableNames() throws IOException {
    return asTableNames(requestTableList().getTablesList());
  }

  /**
   * Request a list of Tables for the cluster.  The {@link Table}s in the response will only
   * contain fully qualified Bigtable table names, and not column family information.
   */
  private ListTablesResponse requestTableList() throws IOException {
    try {
      ListTablesRequest.Builder builder = ListTablesRequest.newBuilder();
      clusterMetadataSetter.setMetadata(builder);
      return bigtableAdminClient.listTables(builder.build());
    } catch (Throwable throwable) {
      throw new IOException("Failed to listTables", throwable);
    }
  }

  /**
   * Convert a list of Bigtable {@link Table}s to hbase {@link TableName}.
   */
  private TableName[] asTableNames(List<Table> tablesList) {
    TableName[] result = new TableName[tablesList.size()];
    for (int i = 0; i < tablesList.size(); i++) {
      // This will contain things like project, zone and cluster.
      String bigtableFullTableName = tablesList.get(i).getName();

      // Strip out the Bigtable info.
      String name = clusterMetadataSetter.toHBaseTableName(bigtableFullTableName);

      result[i] = TableName.valueOf(name);
    }
    return result;
  }

  @Override
  public HTableDescriptor getTableDescriptor(TableName tableName)
      throws TableNotFoundException, IOException {
    if (tableName == null) {
      return null;
    }

    String bigtableTableName = TableMetadataSetter.getBigtableName(tableName, options);
    GetTableRequest request = GetTableRequest.newBuilder().setName(bigtableTableName).build();

    try {
      return tableAdapter.adapt(bigtableAdminClient.getTable(request));
    } catch (UncheckedExecutionException e) {
      if (e.getCause() != null && e.getCause() instanceof OperationRuntimeException) {
        Status status = ((OperationRuntimeException) e.getCause()).getStatus();
        if (status.getCode() == Status.NOT_FOUND.getCode()) {
          throw new TableNotFoundException(tableName);
        }
      }
      throw new IOException("Failed to getTableDescriptor() on " + tableName, e);
    } catch (Throwable throwable) {
      throw new IOException("Failed to getTableDescriptor() on " + tableName, throwable);
    }
  }

  // Used by the Hbase shell but not defined by Admin. Will be removed once the
  // shell is switch to use the methods defined in the interface.
  @Deprecated
  public String[] getTableNames(String regex) throws IOException {
    HTableDescriptor[] tableDescriptors = listTables(regex);
    String[] tableNames = new String[tableDescriptors.length];
    for (int i = 0; i < tableDescriptors.length; i++) {
      tableNames[i] = tableDescriptors[i].getNameAsString();
    }
    return tableNames;
  }

  @Override
  public void createTable(HTableDescriptor desc) throws IOException {
    CreateTableRequest.Builder builder = CreateTableRequest.newBuilder();
    clusterMetadataSetter.setMetadata(builder);
    builder.setTableId(desc.getTableName().getQualifierAsString());
    builder.setTable(tableAdapter.adapt(desc));
    try {
      bigtableAdminClient.createTable(builder.build());
    } catch (Throwable throwable) {
      throw new IOException(
          String.format(
              "Failed to create table '%s'",
              desc.getTableName().getNameAsString()),
          throwable);
    }
  }

  @Override
  public void createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions)
      throws IOException {
    LOG.warn("Creating table, but ignoring parameters startKey, endKey and numRegions");
    createTable(desc);
  }

  @Override
  public void createTable(HTableDescriptor desc, byte[][] splitKeys) throws IOException {
    LOG.warn("Creating table, but ignoring parameters splitKeys");
    createTable(desc);
  }

  @Override
  public void createTableAsync(HTableDescriptor desc, byte[][] splitKeys) throws IOException {
    LOG.warn("Creating the table synchronously");
    createTable(desc);
  }

  @Override
  public void deleteTable(TableName tableName) throws IOException {
    Builder deleteBuilder = DeleteTableRequest.newBuilder();
    TableMetadataSetter.from(tableName, options).setMetadata(deleteBuilder);
    try {
      bigtableAdminClient.deleteTable(deleteBuilder.build());
    } catch (Throwable throwable) {
      throw new IOException(
          String.format(
              "Failed to delete table '%s'",
              tableName.getNameAsString()),
          throwable);
    }
    disabledTables.remove(tableName);
  }

  @Override
  public HTableDescriptor[] deleteTables(String regex) throws IOException {
    return deleteTables(Pattern.compile(regex));
  }

  @Override
  public HTableDescriptor[] deleteTables(Pattern pattern) throws IOException {
    List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
    for (HTableDescriptor table : listTables(pattern)) {
      try {
        deleteTable(table.getTableName());
      } catch (IOException ex) {
        LOG.info("Failed to delete table " + table.getTableName(), ex);
        failed.add(table);
      }
    }
    return failed.toArray(new HTableDescriptor[failed.size()]);
  }

  @Override
  public void truncateTable(TableName tableName, boolean preserveSplits) throws IOException {
    throw new UnsupportedOperationException("truncateTable");  // TODO
  }

  @Override
{'fraction_non_alphanumeric': 0.09262498178108147, 'fraction_numerical': 0.001530389156099694, 'mean_word_length': 4.284999037165415, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '261726', 'n_tokens_mistral': 7447, 'n_tokens_neox': 7299, 'n_words': 1927}