/* * 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 com.analysys.presto.connector.hbase.meta; import com.analysys.presto.connector.hbase.connection.HBaseClientManager; import com.analysys.presto.connector.hbase.frame.HBaseConnectorId; import com.analysys.presto.connector.hbase.utils.Constant; import com.analysys.presto.connector.hbase.utils.Utils; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.log.Logger; import io.airlift.slice.Slice; import io.prestosql.spi.connector.*; import io.prestosql.spi.predicate.TupleDomain; import io.prestosql.spi.statistics.ComputedStatistics; import io.prestosql.spi.type.Type; import io.prestosql.spi.type.VarcharType; import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos; import javax.inject.Inject; import java.io.IOException; import java.util.*; import static com.analysys.presto.connector.hbase.utils.Constant.CONNECTOR_NAME; import static com.analysys.presto.connector.hbase.utils.Types.checkType; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; /** * HBase metadata * * @author wupeng * @date 2019/01/29 */ public class HBaseMetadata implements ConnectorMetadata { private static final Logger log = Logger.get(HBaseMetadata.class); private final HBaseConnectorId connectorId; private final HBaseTables hbaseTables; private final HBaseClientManager hbaseClientManager; @Inject public HBaseMetadata(HBaseConnectorId connectorId, HBaseTables hbaseTables, HBaseClientManager hbaseClientManager) { this.connectorId = connectorId; this.hbaseTables = requireNonNull(hbaseTables, "hbaseTables is null"); this.hbaseClientManager = hbaseClientManager; } @Override public List<String> listSchemaNames(ConnectorSession connectorSession) { return listSchemaNames(); } @Override public ConnectorTableHandle getTableHandle(ConnectorSession connectorSession, SchemaTableName schemaTableName) { requireNonNull(schemaTableName, "schemaTableName is null"); Admin admin = null; ConnectorTableHandle connectorTableHandle; try { admin = hbaseClientManager.getAdmin(); connectorTableHandle = hbaseTables.getTables( admin, schemaTableName.getSchemaName()).get(schemaTableName); } finally { if (admin != null) { hbaseClientManager.close(admin); } } return connectorTableHandle; } @Override public ConnectorTableMetadata getTableMetadata(ConnectorSession connectorSession, ConnectorTableHandle connectorTableHandle) { HBaseTableHandle hBaseTableHandle = (HBaseTableHandle) connectorTableHandle; SchemaTableName tableName = new SchemaTableName(hBaseTableHandle.getSchemaTableName().getSchemaName(), hBaseTableHandle.getSchemaTableName().getTableName()); return this.getTableMetadata(tableName); } private ConnectorTableMetadata getTableMetadata(SchemaTableName tableName) { if (!this.listSchemaNames().contains(tableName.getSchemaName())) { return null; } else { HBaseTable table = hbaseClientManager.getTable(tableName.getSchemaName(), tableName.getTableName()); return table == null ? null : new ConnectorTableMetadata(tableName, table.getColumnsMetadata()); } } private List<String> listSchemaNames() { return ImmutableList.copyOf(hbaseTables.getSchemaNames()); } @Override public List<SchemaTableName> listTables(ConnectorSession connectorSession, Optional<String> schemaName) { Admin admin = hbaseClientManager.getAdmin(); List<SchemaTableName> schemaTableNames; try { schemaTableNames = new ArrayList<>(hbaseTables.getTables(admin, schemaName.orElse("")).keySet()); } finally { if (admin != null) { hbaseClientManager.close(admin); } } return schemaTableNames; } @Override public Map<String, ColumnHandle> getColumnHandles(ConnectorSession connectorSession, ConnectorTableHandle connectorTableHandle) { HBaseTableHandle hBaseTableHandle = (HBaseTableHandle) connectorTableHandle; HBaseTable table = hbaseClientManager.getTable(hBaseTableHandle.getSchemaTableName().getSchemaName(), hBaseTableHandle.getSchemaTableName().getTableName()); if (table == null) { throw new TableNotFoundException(hBaseTableHandle.getSchemaTableName()); } else { ImmutableMap.Builder<String, ColumnHandle> columnHandles = ImmutableMap.builder(); int index = 0; for (Iterator itr = table.getColumnsMetadata().iterator(); itr.hasNext(); ++index) { HBaseColumnMetadata column = (HBaseColumnMetadata) itr.next(); columnHandles.put(column.getName(), new HBaseColumnHandle( connectorId.getId(), column.getFamily(), column.getName(), column.getType(), index, column.isRowKey())); } return columnHandles.build(); } } @Override public ColumnMetadata getColumnMetadata(ConnectorSession connectorSession, ConnectorTableHandle connectorTableHandle, ColumnHandle columnHandle) { checkType(connectorTableHandle, HBaseTableHandle.class, "tableHandle"); return checkType(columnHandle, HBaseColumnHandle.class, "columnHandle").toColumnMetadata(); } @Override public Map<SchemaTableName, List<ColumnMetadata>> listTableColumns(ConnectorSession connectorSession, SchemaTablePrefix schemaTablePrefix) { Objects.requireNonNull(schemaTablePrefix, "prefix is null"); ImmutableMap.Builder<SchemaTableName, List<ColumnMetadata>> columns = ImmutableMap.builder(); List<SchemaTableName> tables = this.listTables(connectorSession, schemaTablePrefix); for (SchemaTableName tableName : tables) { ConnectorTableMetadata tableMetadata = this.getTableMetadata(tableName); if (tableMetadata != null) { columns.put(tableName, tableMetadata.getColumns()); } } return columns.build(); } private List<SchemaTableName> listTables(ConnectorSession session, SchemaTablePrefix prefix) { return (prefix.getSchema().isPresent() ? this.listTables(session, prefix.getSchema()) : ImmutableList.of(new SchemaTableName("", prefix.getTable().orElse("")))); } @Override public void dropTable(ConnectorSession session, ConnectorTableHandle tableHandle) { HBaseTableHandle handle = (HBaseTableHandle) tableHandle; String schema = handle.getSchemaTableName().getSchemaName(); String tableName = handle.getSchemaTableName().getTableName(); hbaseTables.dropTable(schema, tableName); } /** * create snapshot * * @param snapshotName snapshot name * @param admin admin * @param schemaName schema name * @param tableName table name * @throws IOException io exception */ public static void createSnapshot(String snapshotName, Admin admin, String schemaName, String tableName) throws IOException { long start = System.currentTimeMillis(); String fullTableName; if (Constant.HBASE_NAMESPACE_DEFAULT.equals(schemaName) || "".equals(schemaName)) { fullTableName = tableName; } else { fullTableName = schemaName + ":" + tableName; } HBaseProtos.SnapshotDescription snapshot = HBaseProtos.SnapshotDescription.newBuilder() .setName(snapshotName) .setTable(fullTableName) .setType(HBaseProtos.SnapshotDescription.Type.FLUSH) // .setType(HBaseProtos.SnapshotDescription.Type.DISABLED) .build(); admin.snapshot(snapshot); log.info("createSnapshot: create snapshot " + snapshotName + " used " + (System.currentTimeMillis() - start) + " mill seconds."); } // ----------------------------------- start insert ----------------------------------- @Override public ConnectorInsertTableHandle beginInsert(ConnectorSession session, ConnectorTableHandle connectorTableHandle) { HBaseTableHandle tableHandle = fromConnectorTableHandle(connectorTableHandle); String schemaName = tableHandle.getSchemaTableName().getSchemaName(); String tableName = tableHandle.getSchemaTableName().getTableName(); try { TableMetaInfo tableMetaInfo = Utils.getTableMetaInfoFromJson(schemaName, tableName, this.hbaseClientManager.getConfig().getMetaDir()); requireNonNull(tableMetaInfo, String.format("The metadata of table %s.%s is null", schemaName, tableName)); List<ColumnMetaInfo> cols = tableMetaInfo.getColumns(); List<String> columnNames = new ArrayList<>(cols.size()); List<Type> columnTypes = new ArrayList<>(cols.size()); Map<String, String> colNameAndFamilyNameMap = new HashMap<>(); for (ColumnMetaInfo col : cols) { columnNames.add(col.getColumnName()); columnTypes.add(Utils.matchType(col.getType())); colNameAndFamilyNameMap.put(col.getColumnName(), col.getFamily()); } int rowKeyColumnChannel = this.findRowKeyChannel(tableMetaInfo.getColumns()); return new HBaseInsertTableHandle( connectorId.getId(), tableHandle.getSchemaTableName(), columnNames, columnTypes, rowKeyColumnChannel, colNameAndFamilyNameMap); } catch (Exception ex) { log.error(ex.getMessage(), ex); } return null; } private int findRowKeyChannel(List<ColumnMetaInfo> columns) { for (int i = 0; i < columns.size(); i++) { if (columns.get(i).isRowKey()) { return i; } } // Table must specify rowKey column's name. // So it's impossible to be here. return -1; } @Override public Optional<ConnectorOutputMetadata> finishInsert(ConnectorSession session, ConnectorInsertTableHandle insertHandle, Collection<Slice> fragments, Collection<ComputedStatistics> computedStatistics) { return Optional.empty(); } private HBaseTableHandle fromConnectorTableHandle(ConnectorTableHandle tableHandle) { return checkType(tableHandle, HBaseTableHandle.class, "tableHandle"); } // ----------------------------------- end insert ----------------------------------- // --------------- support delete function start --------------- @Override public ColumnHandle getUpdateRowIdColumnHandle(ConnectorSession session, ConnectorTableHandle tableHandle) { HBaseTableHandle hth = (HBaseTableHandle) tableHandle; String schemaName = hth.getSchemaTableName().getSchemaName(); String tableName = hth.getSchemaTableName().getTableName(); TableMetaInfo tableMetaInfo = Utils.getTableMetaInfoFromJson(schemaName, tableName, this.hbaseClientManager.getConfig().getMetaDir()); requireNonNull(tableMetaInfo, String.format("Table %s.%s has no metadata, please check .json file under %s", schemaName, tableName, hbaseClientManager.getConfig().getMetaDir() + "/" + schemaName)); Optional<ColumnMetaInfo> rowKeyOpt = tableMetaInfo.getColumns().stream().filter(ColumnMetaInfo::isRowKey).findFirst(); checkArgument(rowKeyOpt.isPresent(), String.format("Table %s.%s has no rowKey! Please check .json file under %s", schemaName, tableName, hbaseClientManager.getConfig().getMetaDir() + "/" + schemaName)); ColumnMetaInfo rowKeyInfo = rowKeyOpt.get(); // HBaseColumnHandle's attributes cannot be all the same with the REAL rowKey column, // Or there will be a java.lang.IllegalArgumentException: Multiple entries with same value Exception. return new HBaseColumnHandle(CONNECTOR_NAME, "", rowKeyInfo.getColumnName(), VarcharType.VARCHAR, tableMetaInfo.getColumns().indexOf(rowKeyOpt.get()), rowKeyInfo.isRowKey()); } @Override public ConnectorTableHandle beginDelete(ConnectorSession session, ConnectorTableHandle tableHandle) { return fromConnectorTableHandle(tableHandle); } @Override public void finishDelete(ConnectorSession session, ConnectorTableHandle tableHandle, Collection<Slice> fragments) { } @Override public boolean supportsMetadataDelete(ConnectorSession session, ConnectorTableHandle tableHandle, ConnectorTableLayoutHandle tableLayoutHandle) { return false; } // --------------- support delete function end --------------- @Override public ConnectorTableProperties getTableProperties(ConnectorSession session, ConnectorTableHandle table) { return new ConnectorTableProperties(); } @Override public boolean usesLegacyTableLayouts() { return false; } @Override public Optional<ConstraintApplicationResult<ConnectorTableHandle>> applyFilter(ConnectorSession session, ConnectorTableHandle handle, Constraint constraint) { HBaseTableHandle tableHandle = (HBaseTableHandle) handle; TupleDomain<ColumnHandle> oldDomain = tableHandle.getConstraint(); TupleDomain<ColumnHandle> newDomain = oldDomain.intersect(constraint.getSummary()); if (oldDomain.equals(newDomain)) { return Optional.empty(); } tableHandle = new HBaseTableHandle(tableHandle.getSchemaTableName(), newDomain); return Optional.of(new ConstraintApplicationResult<>(tableHandle, constraint.getSummary())); } }