package ml.littlebulb.presto.kudu; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.ColumnMetadata; import com.facebook.presto.spi.ConnectorTableMetadata; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.SchemaNotFoundException; import com.facebook.presto.spi.SchemaTableName; import com.facebook.presto.spi.TableNotFoundException; import com.facebook.presto.spi.predicate.DiscreteValues; import com.facebook.presto.spi.predicate.Domain; import com.facebook.presto.spi.predicate.EquatableValueSet; import com.facebook.presto.spi.predicate.Marker; import com.facebook.presto.spi.predicate.Range; import com.facebook.presto.spi.predicate.Ranges; import com.facebook.presto.spi.predicate.SortedRangeSet; import com.facebook.presto.spi.predicate.TupleDomain; import com.facebook.presto.spi.predicate.ValueSet; import com.facebook.presto.spi.type.DecimalType; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.log.Logger; import ml.littlebulb.presto.kudu.properties.ColumnDesign; import ml.littlebulb.presto.kudu.properties.HashPartitionDefinition; import ml.littlebulb.presto.kudu.properties.KuduTableProperties; import ml.littlebulb.presto.kudu.properties.PartitionDesign; import ml.littlebulb.presto.kudu.properties.RangePartition; import ml.littlebulb.presto.kudu.properties.RangePartitionDefinition; import org.apache.kudu.ColumnSchema; import org.apache.kudu.ColumnTypeAttributes; import org.apache.kudu.Schema; import org.apache.kudu.Type; import org.apache.kudu.client.AlterTableOptions; import org.apache.kudu.client.CreateTableOptions; import org.apache.kudu.client.Delete; import org.apache.kudu.client.Insert; import org.apache.kudu.client.KuduClient; import org.apache.kudu.client.KuduException; import org.apache.kudu.client.KuduPredicate; import org.apache.kudu.client.KuduScanToken; import org.apache.kudu.client.KuduScanner; import org.apache.kudu.client.KuduSession; import org.apache.kudu.client.KuduTable; import org.apache.kudu.client.PartialRow; import org.apache.kudu.client.RowResult; import org.apache.kudu.client.RowResultIterator; import org.apache.kudu.client.SessionConfiguration; import org.apache.kudu.client.Upsert; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_USER_ERROR; import static com.facebook.presto.spi.StandardErrorCode.QUERY_REJECTED; import static com.google.common.collect.ImmutableList.toImmutableList; public class NativeKuduClientSession implements KuduClientSession { public static final String DEFAULT_SCHEMA = "default"; private final Logger log = Logger.get(getClass()); private final KuduConnectorId connectorId; private final String tenantPrefix; private final String rawSchemasTableName; private final KuduClient client; private KuduTable rawSchemasTable; public NativeKuduClientSession(KuduConnectorId connectorId, KuduClient client, String tenant) { this.connectorId = connectorId; this.client = client; this.tenantPrefix = tenant == null ? "" : tenant + "."; this.rawSchemasTableName = "$schemas"; } @Override public List<String> listSchemaNames() { try { if (rawSchemasTable == null) { if (!client.tableExists(rawSchemasTableName)) { createAndFillSchemasTable(); } rawSchemasTable = getSchemasTable(); } ColumnSchema tenantColumn = rawSchemasTable.getSchema().getColumnByIndex(0); KuduScanner scanner = client.newScannerBuilder(rawSchemasTable) .addPredicate(KuduPredicate.newComparisonPredicate(tenantColumn, KuduPredicate.ComparisonOp.EQUAL, tenantPrefix)) .setProjectedColumnIndexes(ImmutableList.of(1)) .build(); RowResultIterator iterator = scanner.nextRows(); ArrayList<String> result = new ArrayList<>(); while (iterator != null) { for (RowResult row : iterator) { result.add(row.getString(0)); } iterator = scanner.nextRows(); } return result; } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public boolean schemaExists(String schemaName) { List<String> list = listSchemaNames(); return list.contains(schemaName); } private KuduTable getSchemasTable() throws KuduException { if (rawSchemasTable == null) { rawSchemasTable = client.openTable(rawSchemasTableName); } return rawSchemasTable; } private void createAndFillSchemasTable() throws KuduException { List<String> existingSchemaNames = listSchemaNamesFromTablets(); ColumnSchema tenantColumnSchema = new ColumnSchema.ColumnSchemaBuilder("tenant", Type.STRING) .key(true).build(); ColumnSchema schemaColumnSchema = new ColumnSchema.ColumnSchemaBuilder("schema", Type.STRING) .key(true).build(); Schema schema = new Schema(ImmutableList.of(tenantColumnSchema, schemaColumnSchema)); CreateTableOptions options = new CreateTableOptions(); options.setNumReplicas(1); // TODO config options.addHashPartitions(ImmutableList.of(tenantColumnSchema.getName()), 2); KuduTable schemasTable = client.createTable(rawSchemasTableName, schema, options); KuduSession session = client.newSession(); session.setFlushMode(SessionConfiguration.FlushMode.AUTO_FLUSH_BACKGROUND); try { for (String schemaName : existingSchemaNames) { Insert insert = schemasTable.newInsert(); fillSchemaRow(insert.getRow(), schemaName); session.apply(insert); } } finally { session.close(); } } private List<String> listSchemaNamesFromTablets() { final String prefix = tenantPrefix; List<String> tables = internalListTables(prefix); LinkedHashSet<String> schemas = new LinkedHashSet<>(); schemas.add(DEFAULT_SCHEMA); for (String table : tables) { int index = table.indexOf('.', prefix.length()); if (index > prefix.length()) { String schema = table.substring(prefix.length(), index); schemas.add(schema); } } return ImmutableList.copyOf(schemas); } private List<String> internalListTables(String prefix) { try { List<String> tables; if (prefix.isEmpty()) { tables = client.getTablesList().getTablesList(); } else { tables = client.getTablesList(prefix).getTablesList().stream(). filter(name -> name.startsWith(prefix)).collect(toImmutableList()); } return tables; } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public List<SchemaTableName> listTables(String schemaNameOrNull) { final int offset = tenantPrefix.length(); final String prefix; if (schemaNameOrNull == null || schemaNameOrNull.equals(DEFAULT_SCHEMA)) { prefix = tenantPrefix; } else { prefix = tenantPrefix + schemaNameOrNull + "."; } List<String> tables = internalListTables(prefix); return tables.stream().map(name -> { int index = name.indexOf('.', offset); if (index > offset) { String schema = name.substring(offset, index); String table = name.substring(index + 1); return new SchemaTableName(schema, table); } else { String schema = DEFAULT_SCHEMA; String table = name.substring(offset); return new SchemaTableName(schema, table); } }).collect(toImmutableList()); } @Override public boolean tableExists(SchemaTableName schemaTableName) { String rawName = toRawName(schemaTableName); try { return client.tableExists(rawName); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public Schema getTableSchema(KuduTableHandle tableHandle) { KuduTable table = tableHandle.getTable(this); return table.getSchema(); } @Override public Map<String, Object> getTableProperties(KuduTableHandle tableHandle) { KuduTable table = tableHandle.getTable(this); return KuduTableProperties.toMap(table); } @Override public List<KuduSplit> buildKuduSplits(KuduTableLayoutHandle layoutHandle) { KuduTableHandle tableHandle = layoutHandle.getTableHandle(); KuduTable table = tableHandle.getTable(this); final int primaryKeyColumnCount = table.getSchema().getPrimaryKeyColumnCount(); KuduScanToken.KuduScanTokenBuilder builder = client.newScanTokenBuilder(table); TupleDomain<ColumnHandle> constraintSummary = layoutHandle.getConstraintSummary(); if (!addConstraintPredicates(table, builder, constraintSummary)) { return ImmutableList.of(); } Optional<Set<ColumnHandle>> desiredColumns = layoutHandle.getDesiredColumns(); if (desiredColumns.isPresent()) { if (desiredColumns.get().contains(KuduColumnHandle.ROW_ID_HANDLE)) { List<Integer> columnIndexes = IntStream .range(0, primaryKeyColumnCount) .boxed().collect(Collectors.toList()); for (ColumnHandle columnHandle : desiredColumns.get()) { if (columnHandle instanceof KuduColumnHandle) { KuduColumnHandle k = (KuduColumnHandle) columnHandle; int index = k.getOrdinalPosition(); if (index >= primaryKeyColumnCount) { columnIndexes.add(index); } } } builder.setProjectedColumnIndexes(columnIndexes); } else { List<Integer> columnIndexes = desiredColumns.get().stream() .map(handle -> ((KuduColumnHandle) handle).getOrdinalPosition()) .collect(toImmutableList()); builder.setProjectedColumnIndexes(columnIndexes); } } List<KuduScanToken> tokens = builder.build(); return tokens.stream() .map(token -> toKuduSplit(tableHandle, token, primaryKeyColumnCount)) .collect(toImmutableList()); } @Override public KuduScanner createScanner(KuduSplit kuduSplit) { try { KuduScanner scanner = KuduScanToken.deserializeIntoScanner(kuduSplit.getPb(), client); return scanner; } catch (IOException e) { throw new RuntimeException(e); } } @Override public KuduTable openTable(SchemaTableName schemaTableName) { String rawName = toRawName(schemaTableName); try { KuduTable table = client.openTable(rawName); return table; } catch (Exception e) { log.debug("Error on doOpenTable: " + e, e); if (!listSchemaNames().contains(schemaTableName.getSchemaName())) { throw new SchemaNotFoundException(schemaTableName.getSchemaName()); } else { throw new TableNotFoundException(schemaTableName); } } } @Override public KuduSession newSession() { return client.newSession(); } @Override public void createSchema(String schemaName) { if (DEFAULT_SCHEMA.equals(schemaName)) { throw new SchemaAlreadyExistsException(schemaName); } else { try { KuduTable schemasTable = getSchemasTable(); KuduSession session = client.newSession(); try { Upsert upsert = schemasTable.newUpsert(); fillSchemaRow(upsert.getRow(), schemaName); session.apply(upsert); } finally { session.close(); } } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } } private void fillSchemaRow(PartialRow row, String schemaName) { row.addString(0, tenantPrefix); row.addString(1, schemaName); } @Override public void dropSchema(String schemaName) { if (DEFAULT_SCHEMA.equals(schemaName)) { throw new PrestoException(GENERIC_USER_ERROR, "Deleting default schema not allowed."); } else { try { for (SchemaTableName table : listTables(schemaName)) { dropTable(table); } KuduTable schemasTable = getSchemasTable(); KuduSession session = client.newSession(); try { Delete delete = schemasTable.newDelete(); fillSchemaRow(delete.getRow(), schemaName); session.apply(delete); } finally { session.close(); } } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } } @Override public void dropTable(SchemaTableName schemaTableName) { try { String rawName = toRawName(schemaTableName); client.deleteTable(rawName); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public void renameTable(SchemaTableName schemaTableName, SchemaTableName newSchemaTableName) { try { String rawName = toRawName(schemaTableName); String newRawName = toRawName(newSchemaTableName); AlterTableOptions alterOptions = new AlterTableOptions(); alterOptions.renameTable(newRawName); client.alterTable(rawName, alterOptions); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public KuduTable createTable(ConnectorTableMetadata tableMetadata, boolean ignoreExisting) { try { SchemaTableName schemeTableName= tableMetadata.getTable(); String rawName = toRawName(schemeTableName); if (ignoreExisting) { if (client.tableExists(rawName)) { return null; } } if(!schemaExists(schemeTableName.getSchemaName())){ throw new SchemaNotFoundException(schemeTableName.getSchemaName()); } List<ColumnMetadata> columns = tableMetadata.getColumns(); Map<String, Object> properties = tableMetadata.getProperties(); Schema schema = buildSchema(columns, properties); CreateTableOptions options = buildCreateTableOptions(schema, properties); return client.createTable(rawName, schema, options); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public void addColumn(SchemaTableName schemaTableName, ColumnMetadata column) { try { String rawName = toRawName(schemaTableName); AlterTableOptions alterOptions = new AlterTableOptions(); Type type = TypeHelper.toKuduClientType(column.getType()); alterOptions.addNullableColumn(column.getName(), type); client.alterTable(rawName, alterOptions); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public void dropColumn(SchemaTableName schemaTableName, String name) { try { String rawName = toRawName(schemaTableName); AlterTableOptions alterOptions = new AlterTableOptions(); alterOptions.dropColumn(name); client.alterTable(rawName, alterOptions); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } @Override public void renameColumn(SchemaTableName schemaTableName, String oldName, String newName) { try { String rawName = toRawName(schemaTableName); AlterTableOptions alterOptions = new AlterTableOptions(); alterOptions.renameColumn(oldName, newName); client.alterTable(rawName, alterOptions); } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } private enum RangePartitionChange { ADD, DROP } @Override public void addRangePartition(SchemaTableName schemaTableName, RangePartition rangePartition) { changeRangePartition(schemaTableName, rangePartition, RangePartitionChange.ADD); } @Override public void dropRangePartition(SchemaTableName schemaTableName, RangePartition rangePartition) { changeRangePartition(schemaTableName, rangePartition, RangePartitionChange.DROP); } private void changeRangePartition(SchemaTableName schemaTableName, RangePartition rangePartition, RangePartitionChange change) { try { String rawName = toRawName(schemaTableName); KuduTable table = client.openTable(rawName); Schema schema = table.getSchema(); PartitionDesign design = KuduTableProperties.getPartitionDesign(table); RangePartitionDefinition definition = design.getRange(); if (definition == null) { throw new PrestoException(QUERY_REJECTED, "Table " + schemaTableName + " has no range partition"); } PartialRow lowerBound = KuduTableProperties.toRangeBoundToPartialRow(schema, definition, rangePartition.getLower()); PartialRow upperBound = KuduTableProperties.toRangeBoundToPartialRow(schema, definition, rangePartition.getUpper()); AlterTableOptions alterOptions = new AlterTableOptions(); switch (change) { case ADD: alterOptions.addRangePartition(lowerBound, upperBound); break; case DROP: alterOptions.dropRangePartition(lowerBound, upperBound); break; } client.alterTable(rawName, alterOptions); } catch (PrestoException e) { throw e; } catch (KuduException e) { throw new PrestoException(GENERIC_INTERNAL_ERROR, e); } } private Schema buildSchema(List<ColumnMetadata> columns, Map<String, Object> properties) { Optional<Map<String, ColumnDesign>> optColumnDesign = KuduTableProperties.getColumnDesign(properties); Map<String, ColumnDesign> columnDesignMap = optColumnDesign.orElse(ImmutableMap.of()); List<ColumnSchema> kuduColumns = columns.stream() .map(columnMetadata -> toColumnSchema(columnMetadata, columnDesignMap)) .collect(ImmutableList.toImmutableList()); return new Schema(kuduColumns); } private ColumnSchema toColumnSchema(ColumnMetadata columnMetadata, Map<String, ColumnDesign> columnDesignMap) { String name = columnMetadata.getName(); ColumnDesign design = columnDesignMap.getOrDefault(name, ColumnDesign.DEFAULT); Type ktype = TypeHelper.toKuduClientType(columnMetadata.getType()); ColumnSchema.ColumnSchemaBuilder builder = new ColumnSchema.ColumnSchemaBuilder(name, ktype); builder.key(design.isKey()).nullable(design.isNullable()); setEncoding(name, builder, design); setCompression(name, builder, design); setTypeAttributes(columnMetadata, builder); return builder.build(); } private void setTypeAttributes(ColumnMetadata columnMetadata, ColumnSchema.ColumnSchemaBuilder builder) { if (columnMetadata.getType() instanceof DecimalType) { DecimalType type = (DecimalType) columnMetadata.getType(); ColumnTypeAttributes attributes = new ColumnTypeAttributes.ColumnTypeAttributesBuilder() .precision(type.getPrecision()) .scale(type.getScale()).build(); builder.typeAttributes(attributes); } } private void setCompression(String name, ColumnSchema.ColumnSchemaBuilder builder, ColumnDesign design) { if (design.getCompression() != null) { try { ColumnSchema.CompressionAlgorithm algorithm = ColumnSchema.CompressionAlgorithm.valueOf(design.getCompression().toUpperCase()); builder.compressionAlgorithm(algorithm); } catch (IllegalArgumentException e) { throw new RuntimeException("Unknown compression algorithm " + design.getCompression() + " for column " + name); } } } private void setEncoding(String name, ColumnSchema.ColumnSchemaBuilder builder, ColumnDesign design) { if (design.getEncoding() != null) { try { ColumnSchema.Encoding encoding = ColumnSchema.Encoding.valueOf(design.getEncoding().toUpperCase()); builder.encoding(encoding); } catch (IllegalArgumentException e) { throw new RuntimeException("Unknown encoding " + design.getEncoding() + " for column " + name); } } } private CreateTableOptions buildCreateTableOptions(Schema schema, Map<String, Object> properties) { CreateTableOptions options = new CreateTableOptions(); RangePartitionDefinition rangePartitionDefinition = null; Optional<PartitionDesign> optPartitionDesign = KuduTableProperties.getPartitionDesign(properties); if (optPartitionDesign.isPresent()) { PartitionDesign partitionDesign = optPartitionDesign.get(); if (partitionDesign.getHash() != null) { for (HashPartitionDefinition partition : partitionDesign.getHash()) { options.addHashPartitions(partition.getColumns(), partition.getBuckets()); } } if (partitionDesign.getRange() != null) { rangePartitionDefinition = partitionDesign.getRange(); options.setRangePartitionColumns(rangePartitionDefinition.getColumns()); } } else { String firstColumn = schema.getColumnByIndex(0).getName(); options.setRangePartitionColumns(Collections.singletonList(firstColumn)); } List<RangePartition> rangePartitions = KuduTableProperties.getRangePartitions(properties); if (rangePartitionDefinition != null && !rangePartitions.isEmpty()) { for (RangePartition rangePartition: rangePartitions) { PartialRow lower = KuduTableProperties.toRangeBoundToPartialRow(schema, rangePartitionDefinition, rangePartition.getLower()); PartialRow upper = KuduTableProperties.toRangeBoundToPartialRow(schema, rangePartitionDefinition, rangePartition.getUpper()); options.addRangePartition(lower, upper); } } Optional<Integer> numReplicas = KuduTableProperties.getNumReplicas(properties); numReplicas.ifPresent(options::setNumReplicas); return options; } /** * translates TupleDomain to KuduPredicates. * * @return false if TupleDomain or one of its domains is none */ private boolean addConstraintPredicates(KuduTable table, KuduScanToken.KuduScanTokenBuilder builder, TupleDomain<ColumnHandle> constraintSummary) { if (constraintSummary.isNone()) { return false; } else if (!constraintSummary.isAll()) { Schema schema = table.getSchema(); for (TupleDomain.ColumnDomain<ColumnHandle> columnDomain : constraintSummary.getColumnDomains().get()) { int position = ((KuduColumnHandle) columnDomain.getColumn()).getOrdinalPosition(); ColumnSchema columnSchema = schema.getColumnByIndex(position); Domain domain = columnDomain.getDomain(); if (domain.isNone()) { return false; } else if (domain.isAll()) { // no restriction } else if (domain.isOnlyNull()) { builder.addPredicate(KuduPredicate.newIsNullPredicate(columnSchema)); } else if (domain.getValues().isAll() && domain.isNullAllowed()) { builder.addPredicate(KuduPredicate.newIsNotNullPredicate(columnSchema)); } else if (domain.isSingleValue()) { KuduPredicate predicate = createEqualsPredicate(columnSchema, domain.getSingleValue()); builder.addPredicate(predicate); } else { ValueSet valueSet = domain.getValues(); if (valueSet instanceof EquatableValueSet) { DiscreteValues discreteValues = valueSet.getDiscreteValues(); KuduPredicate predicate = createInListPredicate(columnSchema, discreteValues); builder.addPredicate(predicate); } else if (valueSet instanceof SortedRangeSet) { Ranges ranges = ((SortedRangeSet) valueSet).getRanges(); Range span = ranges.getSpan(); Marker low = span.getLow(); if (!low.isLowerUnbounded()) { KuduPredicate.ComparisonOp op = (low.getBound() == Marker.Bound.ABOVE) ? KuduPredicate.ComparisonOp.GREATER : KuduPredicate.ComparisonOp.GREATER_EQUAL; KuduPredicate predicate = createComparisonPredicate(columnSchema, op, low.getValue()); builder.addPredicate(predicate); } Marker high = span.getHigh(); if (!high.isUpperUnbounded()) { KuduPredicate.ComparisonOp op = (low.getBound() == Marker.Bound.BELOW) ? KuduPredicate.ComparisonOp.LESS : KuduPredicate.ComparisonOp.LESS_EQUAL; KuduPredicate predicate = createComparisonPredicate(columnSchema, op, high.getValue()); builder.addPredicate(predicate); } } else { throw new IllegalStateException("Unexpected domain: " + domain); } } } } return true; } private KuduPredicate createInListPredicate(ColumnSchema columnSchema, DiscreteValues discreteValues) { com.facebook.presto.spi.type.Type type = TypeHelper.fromKuduColumn(columnSchema); List<Object> javaValues = discreteValues.getValues().stream().map(value -> TypeHelper.getJavaValue(type, value)).collect(toImmutableList()); return KuduPredicate.newInListPredicate(columnSchema, javaValues); } private KuduPredicate createEqualsPredicate(ColumnSchema columnSchema, Object value) { return createComparisonPredicate(columnSchema, KuduPredicate.ComparisonOp.EQUAL, value); } private KuduPredicate createComparisonPredicate(ColumnSchema columnSchema, KuduPredicate.ComparisonOp op, Object value) { com.facebook.presto.spi.type.Type type = TypeHelper.fromKuduColumn(columnSchema); Object javaValue = TypeHelper.getJavaValue(type, value); if (javaValue instanceof Long) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Long) javaValue); } else if (javaValue instanceof Integer) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Integer) javaValue); } else if (javaValue instanceof Short) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Short) javaValue); } else if (javaValue instanceof Byte) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Byte) javaValue); } else if (javaValue instanceof String) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (String) javaValue); } else if (javaValue instanceof Double) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Double) javaValue); } else if (javaValue instanceof Float) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Float) javaValue); } else if (javaValue instanceof Boolean) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (Boolean) javaValue); } else if (javaValue instanceof byte[]) { return KuduPredicate.newComparisonPredicate(columnSchema, op, (byte[]) javaValue); } else if (javaValue == null) { throw new IllegalStateException("Unexpected null java value for column " + columnSchema.getName()); } else { throw new IllegalStateException("Unexpected java value for column " + columnSchema.getName() + ": " + javaValue + "(" + javaValue.getClass() + ")"); } } private KuduSplit toKuduSplit(KuduTableHandle tableHandle, KuduScanToken token, int primaryKeyColumnCount) { try { byte[] pb = token.serialize(); return new KuduSplit(tableHandle, primaryKeyColumnCount, pb); } catch (IOException e) { throw new RuntimeException(e); } } private String toRawName(SchemaTableName schemaTableName) { String rawName; if (schemaTableName.getSchemaName().equals(DEFAULT_SCHEMA)) { rawName = tenantPrefix + schemaTableName.getTableName(); } else { rawName = tenantPrefix + schemaTableName.getSchemaName() + "." + schemaTableName.getTableName(); } return rawName; } }