blob: 09f8cee30533bc02b1969317256bc233749281aa [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.drill.exec.store.ischema;
import org.apache.calcite.avatica.util.TimeUnit;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.drill.common.types.TypeProtos;
import org.apache.drill.common.types.Types;
import org.apache.drill.exec.planner.types.DrillRelDataTypeSystem;
import org.apache.drill.exec.record.metadata.ColumnMetadata;
import org.apache.drill.exec.store.dfs.WorkspaceSchemaFactory;
import org.apache.drill.metastore.metadata.BaseTableMetadata;
import org.apache.drill.metastore.metadata.PartitionMetadata;
import org.apache.drill.metastore.metadata.SegmentMetadata;
import org.apache.drill.metastore.statistics.ColumnStatistics;
import org.apache.drill.metastore.statistics.ColumnStatisticsKind;
import org.apache.drill.metastore.statistics.Statistic;
import org.apache.drill.metastore.statistics.TableStatisticsKind;
import org.apache.drill.shaded.guava.com.google.common.base.MoreObjects;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.slf4j.Logger;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
import java.util.stream.Collectors;
import static org.slf4j.LoggerFactory.getLogger;
public class Records {
public static final String YES = "YES";
public static final String NO = "NO";
/**
* Converts boolean value into its String representation.
*
* @param value boolean value
* @return boolean value String representation
*/
public static String convertToString(boolean value) {
return value ? YES : NO;
}
/**
* Converts given time millis into {@link Timestamp} instance.
*
* @param millis time millis
* @return {@link Timestamp} instance
*/
public static Timestamp convertToTimestamp(long millis) {
return millis == BaseTableMetadata.UNDEFINED_TIME
? null
: Timestamp.from(Instant.ofEpochMilli(millis));
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.TABLES
*/
public static class Table {
public final String TABLE_CATALOG;
public final String TABLE_SCHEMA;
public final String TABLE_NAME;
public final String TABLE_TYPE;
public final String TABLE_SOURCE;
public final String LOCATION;
public final Long NUM_ROWS;
public final Timestamp LAST_MODIFIED_TIME;
public Table(String catalog, String schema, String name, String type) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schema;
this.TABLE_NAME = name;
this.TABLE_TYPE = type;
this.TABLE_SOURCE = null;
this.LOCATION = null;
this.NUM_ROWS = null;
this.LAST_MODIFIED_TIME = null;
}
public Table(String catalog, String schema, String type, BaseTableMetadata tableMetadata) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schema;
this.TABLE_NAME = tableMetadata.getTableInfo().name();
this.TABLE_TYPE = type;
this.TABLE_SOURCE = tableMetadata.getTableInfo().type();
this.LOCATION = tableMetadata.getLocation().toString();
this.NUM_ROWS = tableMetadata.getStatistic(TableStatisticsKind.ROW_COUNT);
this.LAST_MODIFIED_TIME = convertToTimestamp(tableMetadata.getLastModifiedTime());
}
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.COLUMNS
*/
public static class Column {
private static final Logger logger = getLogger(Column.class);
private static final int MAX_UTF8_BYTES_PER_CHARACTER = 4;
public final String TABLE_CATALOG;
public final String TABLE_SCHEMA;
public final String TABLE_NAME;
public final String COLUMN_NAME;
public final int ORDINAL_POSITION;
public final String COLUMN_DEFAULT;
public final String IS_NULLABLE;
public final String DATA_TYPE;
public final Integer CHARACTER_MAXIMUM_LENGTH;
public final Integer CHARACTER_OCTET_LENGTH;
public final Integer NUMERIC_PRECISION;
public final Integer NUMERIC_PRECISION_RADIX;
public final Integer NUMERIC_SCALE;
public final Integer DATETIME_PRECISION;
public final String INTERVAL_TYPE;
public final Integer INTERVAL_PRECISION;
public final Integer COLUMN_SIZE;
public final String COLUMN_FORMAT;
public final Long NUM_NULLS;
public final String MIN_VAL;
public final String MAX_VAL;
public final Double NDV;
public final Double EST_NUM_NON_NULLS;
public final boolean IS_NESTED;
// See:
// - ISO/IEC 9075-11:2011(E) 5.21 COLUMNS view
// - ISO/IEC 9075-11:2011(E) 6.22 DATA_TYPE_DESCRIPTOR base table
public Column(String catalog, String schemaName, String tableName, RelDataTypeField field) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schemaName;
this.TABLE_NAME = tableName;
this.COLUMN_NAME = field.getName();
final RelDataType relDataType = field.getType();
// (Like SQL data type names, but not standard ones.)
final SqlTypeName sqlTypeName = relDataType.getSqlTypeName();
// Get 1-based column ordinal position from 0-based field/column index:
this.ORDINAL_POSITION = 1 + field.getIndex();
this.COLUMN_DEFAULT = null;
this.IS_NULLABLE = convertToString(relDataType.isNullable());
this.IS_NESTED = false;
switch (sqlTypeName) {
// 1. SqlTypeName enumerators whose names (currently) match SQL's values
// for DATA_TYPE (those which have been seen in tests and verified):
case BOOLEAN:
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
case DECIMAL:
case FLOAT:
case REAL:
case DOUBLE:
case DATE:
case TIME:
case TIMESTAMP:
// INTERVAL_YEAR_MONTH - Not identical; see below.
// INTERVAL_DAY_TIME - Not identical; see below.
// CHAR - Not identical; see below.
// VARCHAR - Not identical; see below.
case BINARY:
// VARBINARY - Not identical; see below.
// TODO(DRILL-3253): Update these once we have test plugin supporting
// all needed types:
// NULL - Not seen/explicitly addressed.
// ANY - " "
// SYMBOL - " "
// MULTISET - " "
case ARRAY:
case MAP:
// DISTINCT - Not seen/explicitly addressed.
// STRUCTURED - " "
// ROW - " "
// OTHER - " "
// CURSOR - " "
// COLUMN_LIST - " "
this.DATA_TYPE = sqlTypeName.name();
break;
// 2. SqlTypeName enumerators whose names (currently) do not match SQL's
// values for DATA_TYPE:
case CHAR:
this.DATA_TYPE = "CHARACTER";
break;
case VARCHAR:
this.DATA_TYPE = "CHARACTER VARYING";
break;
case VARBINARY:
this.DATA_TYPE = "BINARY VARYING";
break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
this.DATA_TYPE = "INTERVAL";
break;
// 3: SqlTypeName enumerators not yet seen and confirmed or handled.
default:
logger.warn("Type not handled explicitly (code needs review): "
+ sqlTypeName);
this.DATA_TYPE = sqlTypeName.toString();
break;
}
// Note: The branches are in the same order as SQL constraint
// DATA_TYPE_DESCRIPTOR_DATA_TYPE_CHECK_COMBINATIONS.
switch (sqlTypeName) {
case CHAR:
case VARCHAR:
this.CHARACTER_MAXIMUM_LENGTH = relDataType.getPrecision();
if (this.CHARACTER_MAXIMUM_LENGTH
< Integer.MAX_VALUE / MAX_UTF8_BYTES_PER_CHARACTER) {
this.CHARACTER_OCTET_LENGTH =
this.CHARACTER_MAXIMUM_LENGTH * MAX_UTF8_BYTES_PER_CHARACTER;
}
else {
this.CHARACTER_OCTET_LENGTH = Integer.MAX_VALUE;
}
// Column size is the number of characters
this.COLUMN_SIZE = this.CHARACTER_MAXIMUM_LENGTH;
this.NUMERIC_PRECISION = null;
this.NUMERIC_PRECISION_RADIX = null;
this.NUMERIC_SCALE = null;
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
break;
case BINARY:
case VARBINARY:
this.CHARACTER_MAXIMUM_LENGTH = relDataType.getPrecision();
this.CHARACTER_OCTET_LENGTH = this.CHARACTER_MAXIMUM_LENGTH;
// Column size is the number of bytes
this.COLUMN_SIZE = this.CHARACTER_MAXIMUM_LENGTH;
this.NUMERIC_PRECISION = null;
this.NUMERIC_PRECISION_RADIX = null;
this.NUMERIC_SCALE = null;
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
break;
case BOOLEAN:
this.COLUMN_SIZE = 1;
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
this.NUMERIC_PRECISION = null;
this.NUMERIC_PRECISION_RADIX = null;
this.NUMERIC_SCALE = null;
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
break;
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
// This NUMERIC_PRECISION is in bits since NUMERIC_PRECISION_RADIX is 2.
switch (sqlTypeName) {
case TINYINT:
NUMERIC_PRECISION = 8;
break;
case SMALLINT:
NUMERIC_PRECISION = 16;
break;
case INTEGER:
NUMERIC_PRECISION = 32;
break;
case BIGINT:
NUMERIC_PRECISION = 64;
break;
default:
throw new AssertionError(
"Unexpected " + sqlTypeName.getClass().getName() + " value "
+ sqlTypeName );
//break;
}
this.NUMERIC_PRECISION_RADIX = 2;
// Column size is the number of digits, based on the precision radix
this.COLUMN_SIZE = NUMERIC_PRECISION;
this.NUMERIC_SCALE = 0;
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
break;
case DECIMAL:
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
// This NUMERIC_PRECISION is in decimal digits since
// NUMERIC_PRECISION_RADIX is 10.
this.NUMERIC_PRECISION = relDataType.getPrecision();
this.NUMERIC_PRECISION_RADIX = 10;
// Column size is the number of digits, based on the precision radix
this.COLUMN_SIZE = NUMERIC_PRECISION;
this.NUMERIC_SCALE = relDataType.getScale();
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
break;
case REAL:
case FLOAT:
case DOUBLE:
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
// This NUMERIC_PRECISION is in bits since NUMERIC_PRECISION_RADIX is 2.
switch (sqlTypeName) {
case REAL:
NUMERIC_PRECISION = 24;
break;
case FLOAT:
NUMERIC_PRECISION = 24;
break;
case DOUBLE:
NUMERIC_PRECISION = 53;
break;
default:
throw new AssertionError(
"Unexpected type " + sqlTypeName + " in approximate-types branch" );
//break;
}
this.NUMERIC_PRECISION_RADIX = 2;
// Column size is the number of digits, based on the precision radix
this.COLUMN_SIZE = NUMERIC_PRECISION;
this.NUMERIC_SCALE = null;
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
break;
case DATE:
case TIME:
case TIMESTAMP:
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
this.NUMERIC_PRECISION = null;
this.NUMERIC_PRECISION_RADIX = null;
this.NUMERIC_SCALE = null;
// TODO: Resolve whether this gets _SQL_-defined precision.
// (RelDataType.getPrecision()'s doc. says "JDBC-defined
// precision.")
this.DATETIME_PRECISION = relDataType.getPrecision();
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
switch (sqlTypeName) {
case DATE:
this.COLUMN_SIZE = 10;
break;// yyyy-MM-dd
case TIME: this.COLUMN_SIZE = this.DATETIME_PRECISION == 0
? 8 // HH::mm::ss
: 8 + 1 + this.DATETIME_PRECISION;
break;
case TIMESTAMP: this.COLUMN_SIZE = this.DATETIME_PRECISION == 0
? 10 + 1 + 8 // date + "T" + time
: 10 + 1 + 8 + 1 + this.DATETIME_PRECISION;
break;
default:
throw new AssertionError(
"Unexpected type " + sqlTypeName + " in approximate-types branch" );
}
break;
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
this.NUMERIC_PRECISION = null;
this.NUMERIC_PRECISION_RADIX = null;
this.NUMERIC_SCALE = null;
switch (sqlTypeName) {
case INTERVAL_YEAR:
case INTERVAL_YEAR_MONTH:
case INTERVAL_MONTH:
// NOTE: Apparently can't get use RelDataType, etc.; it seems to
// apply a default fractional seconds precision of 6 for SECOND,
// even though SECOND does not exist for this case.
this.DATETIME_PRECISION = 0;
break;
case INTERVAL_DAY:
case INTERVAL_DAY_HOUR:
case INTERVAL_DAY_MINUTE:
case INTERVAL_DAY_SECOND:
case INTERVAL_HOUR:
case INTERVAL_HOUR_MINUTE:
case INTERVAL_HOUR_SECOND:
case INTERVAL_MINUTE:
case INTERVAL_MINUTE_SECOND:
case INTERVAL_SECOND:
this.DATETIME_PRECISION =
relDataType
.getIntervalQualifier()
.getFractionalSecondPrecision(
DrillRelDataTypeSystem.DRILL_REL_DATATYPE_SYSTEM);
break;
default:
throw new AssertionError(
"Unexpected type " + sqlTypeName + " in interval-types branch");
}
this.INTERVAL_PRECISION =
relDataType
.getIntervalQualifier()
.getStartPrecision(DrillRelDataTypeSystem.DRILL_REL_DATATYPE_SYSTEM);
{
final TimeUnit start = relDataType.getIntervalQualifier().getStartUnit();
// NOTE: getEndUnit() returns null instead of YEAR for "INTERVAL YEAR".
final TimeUnit end = MoreObjects.firstNonNull(relDataType.getIntervalQualifier().getEndUnit(), start);
if (start == end) {
this.INTERVAL_TYPE = start.name();
}
else {
this.INTERVAL_TYPE = start + " TO " + end;
}
// extra size for fractional types
final int extraSecondIntervalSize = this.DATETIME_PRECISION > 0
? DATETIME_PRECISION + 1 // add 1 for decimal point
: 0;
switch (start) {
case YEAR:
switch(end) {
case YEAR:
this.COLUMN_SIZE = INTERVAL_PRECISION + 2;
break;// P..Y
case MONTH:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 5;
break; // P..Y12M
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
break;
case MONTH:
switch (end) {
case MONTH:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 2;
break; // P..M
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
break;
case DAY:
switch (end) {
case DAY:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 2;
break; // P..D
case HOUR:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 6;
break; // P..DT12H
case MINUTE:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 9;
break; // P..DT12H60M
case SECOND:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 12 + extraSecondIntervalSize;
break; // P..DT12H60M60....S
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
break;
case HOUR:
switch (end) {
case HOUR:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 3;
break; // PT..H
case MINUTE:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 6;
break; // PT..H60M
case SECOND:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 9 + extraSecondIntervalSize;
break; // PT..H12M60....S
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
break;
case MINUTE:
switch (end) {
case MINUTE:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 3;
break; // PT...M
case SECOND:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 6 + extraSecondIntervalSize;
break; // PT..M60....S
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
break;
case SECOND:
switch (end) {
case SECOND:
this.COLUMN_SIZE = this.INTERVAL_PRECISION + 3 + extraSecondIntervalSize;
break; // PT....S
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
break;
default:
throw new AssertionError("Unexpected interval type " + this.INTERVAL_TYPE + " in interval-types branch" );
}
}
break;
default:
this.NUMERIC_PRECISION_RADIX = null;
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
this.NUMERIC_PRECISION = null;
this.NUMERIC_SCALE = null;
this.DATETIME_PRECISION = null;
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
this.COLUMN_SIZE = null;
break;
}
this.COLUMN_FORMAT = null;
this.NUM_NULLS = null;
this.MIN_VAL = null;
this.MAX_VAL = null;
this.NDV = null;
this.EST_NUM_NON_NULLS = null;
}
public Column(String catalog, String schemaName, String tableName, String columnName,
ColumnMetadata columnMetadata, ColumnStatistics columnStatistics, int index,
boolean isNested) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schemaName;
this.TABLE_NAME = tableName;
this.COLUMN_NAME = columnName;
this.ORDINAL_POSITION = index + 1;
this.COLUMN_DEFAULT = columnMetadata.defaultValue();
this.IS_NULLABLE = convertToString(columnMetadata.isNullable());
this.COLUMN_FORMAT = columnMetadata.format();
this.IS_NESTED = isNested;
TypeProtos.MajorType type = columnMetadata.majorType();
switch (type.getMinorType()) {
case INTERVAL:
case INTERVALDAY:
case INTERVALYEAR:
this.DATA_TYPE = TypeProtos.MinorType.INTERVAL.name();
break;
default:
this.DATA_TYPE = Types.getSqlTypeName(type);
}
int columnSize = Types.getJdbcDisplaySize(type);
this.COLUMN_SIZE = columnSize == 0 && Types.isScalarStringType(type) ? Types.MAX_VARCHAR_LENGTH : columnSize;
if (Types.isScalarStringType(type)) {
this.CHARACTER_MAXIMUM_LENGTH = COLUMN_SIZE;
this.CHARACTER_OCTET_LENGTH = COLUMN_SIZE;
} else {
this.CHARACTER_MAXIMUM_LENGTH = null;
this.CHARACTER_OCTET_LENGTH = null;
}
if (Types.isNumericType(type)) {
this.NUMERIC_PRECISION = type.getPrecision();
this.NUMERIC_PRECISION_RADIX = Types.isDecimalType(type) ? 10 : 2;
this.NUMERIC_SCALE = type.getScale();
} else {
this.NUMERIC_PRECISION = null;
this.NUMERIC_PRECISION_RADIX = null;
this.NUMERIC_SCALE = null;
}
if (Types.isDateTimeType(type)) {
this.DATETIME_PRECISION = COLUMN_SIZE;
} else {
this.DATETIME_PRECISION = null;
}
if (Types.isIntervalType(type)) {
this.INTERVAL_TYPE = Types.getSqlTypeName(type);
this.INTERVAL_PRECISION = 0;
} else {
this.INTERVAL_TYPE = null;
this.INTERVAL_PRECISION = null;
}
if (columnStatistics == null) {
this.NUM_NULLS = null;
this.MIN_VAL = null;
this.MAX_VAL = null;
this.NDV = null;
this.EST_NUM_NON_NULLS = null;
} else {
Long numNulls = ColumnStatisticsKind.NULLS_COUNT.getFrom(columnStatistics);
this.NUM_NULLS = numNulls == Statistic.NO_COLUMN_STATS ? null : numNulls;
Object minVal = ColumnStatisticsKind.MIN_VALUE.getFrom(columnStatistics);
this.MIN_VAL = minVal == null ? null : minVal.toString();
Object maxVal = ColumnStatisticsKind.MAX_VALUE.getFrom(columnStatistics);
this.MAX_VAL = maxVal == null ? null : maxVal.toString();
this.NDV = ColumnStatisticsKind.NDV.getFrom(columnStatistics);
this.EST_NUM_NON_NULLS = ColumnStatisticsKind.NON_NULL_COUNT.getFrom(columnStatistics);
}
}
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.VIEWS
*/
public static class View {
public final String TABLE_CATALOG;
public final String TABLE_SCHEMA;
public final String TABLE_NAME;
public final String VIEW_DEFINITION;
public View(String catalog, String schema, String name, String definition) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schema;
this.TABLE_NAME = name;
this.VIEW_DEFINITION = definition;
}
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.CATALOGS
*/
public static class Catalog {
public final String CATALOG_NAME;
public final String CATALOG_DESCRIPTION;
public final String CATALOG_CONNECT;
public Catalog(String name, String description, String connect) {
this.CATALOG_NAME = name;
this.CATALOG_DESCRIPTION = description;
this.CATALOG_CONNECT = connect;
}
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.SCHEMATA
*/
public static class Schema {
public final String CATALOG_NAME;
public final String SCHEMA_NAME;
public final String SCHEMA_OWNER;
public final String TYPE;
public final String IS_MUTABLE;
public Schema(String catalog, String name, String owner, String type, boolean isMutable) {
this.CATALOG_NAME = catalog;
this.SCHEMA_NAME = name;
this.SCHEMA_OWNER = owner;
this.TYPE = type;
this.IS_MUTABLE = convertToString(isMutable);
}
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.PARTITIONS
*/
public static class Partition {
public final String TABLE_CATALOG;
public final String TABLE_SCHEMA;
public final String TABLE_NAME;
public final String METADATA_KEY;
public final String METADATA_TYPE;
public final String METADATA_IDENTIFIER;
public final String PARTITION_COLUMN;
public final String PARTITION_VALUE;
public final String LOCATION;
public final Timestamp LAST_MODIFIED_TIME;
public Partition(String catalog, String schemaName, String value, SegmentMetadata segmentMetadata) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schemaName;
this.TABLE_NAME = segmentMetadata.getTableInfo().name();
this.METADATA_KEY = segmentMetadata.getMetadataInfo().key();
this.METADATA_TYPE = segmentMetadata.getMetadataInfo().type().name();
this.METADATA_IDENTIFIER = segmentMetadata.getMetadataInfo().identifier();
this.PARTITION_COLUMN = segmentMetadata.getColumn().toString();
this.PARTITION_VALUE = value;
this.LOCATION = segmentMetadata.getLocation().toString();
this.LAST_MODIFIED_TIME = convertToTimestamp(segmentMetadata.getLastModifiedTime());
}
public Partition(String catalog, String schemaName, String value, PartitionMetadata partitionMetadata) {
this.TABLE_CATALOG = catalog;
this.TABLE_SCHEMA = schemaName;
this.TABLE_NAME = partitionMetadata.getTableInfo().name();
this.METADATA_KEY = partitionMetadata.getMetadataInfo().key();
this.METADATA_TYPE = partitionMetadata.getMetadataInfo().type().name();
this.METADATA_IDENTIFIER = partitionMetadata.getMetadataInfo().identifier();
this.PARTITION_COLUMN = partitionMetadata.getColumn().toString();
this.PARTITION_VALUE = value;
this.LOCATION = null;
this.LAST_MODIFIED_TIME = convertToTimestamp(partitionMetadata.getLastModifiedTime());
}
public static List<Partition> fromSegment(String catalog, String schemaName, SegmentMetadata segmentMetadata) {
return segmentMetadata.getPartitionValues().stream()
.map(value -> new Partition(catalog, schemaName, value, segmentMetadata))
.collect(Collectors.toList());
}
public static List<Partition> fromPartition(String catalog, String schemaName, PartitionMetadata partitionMetadata) {
return partitionMetadata.getPartitionValues().stream()
.map(value -> new Partition(catalog, schemaName, value, partitionMetadata))
.collect(Collectors.toList());
}
}
/**
* Pojo object for a record in INFORMATION_SCHEMA.FILES
*/
public static class File {
public final String SCHEMA_NAME;
public final String ROOT_SCHEMA_NAME;
public final String WORKSPACE_NAME;
public final String FILE_NAME;
public final String RELATIVE_PATH;
public final boolean IS_DIRECTORY;
public final boolean IS_FILE;
public final long LENGTH;
public final String OWNER;
public final String GROUP;
public final String PERMISSION;
public final Timestamp ACCESS_TIME;
public final Timestamp MODIFICATION_TIME;
public File(String schemaName, WorkspaceSchemaFactory.WorkspaceSchema wsSchema, FileStatus fileStatus) {
this.SCHEMA_NAME = schemaName;
this.ROOT_SCHEMA_NAME = wsSchema.getSchemaPath().get(0);
this.WORKSPACE_NAME = wsSchema.getName();
this.FILE_NAME = fileStatus.getPath().getName();
this.RELATIVE_PATH = Path.getPathWithoutSchemeAndAuthority(new Path(wsSchema.getDefaultLocation())).toUri()
.relativize(Path.getPathWithoutSchemeAndAuthority(fileStatus.getPath()).toUri()).getPath();
this.IS_DIRECTORY = fileStatus.isDirectory();
this.IS_FILE = fileStatus.isFile();
this.LENGTH = fileStatus.getLen();
this.OWNER = fileStatus.getOwner();
this.GROUP = fileStatus.getGroup();
this.PERMISSION = fileStatus.getPermission().toString();
this.ACCESS_TIME = getTimestampWithReplacedZone(fileStatus.getAccessTime());
this.MODIFICATION_TIME = getTimestampWithReplacedZone(fileStatus.getModificationTime());
}
/**
* Convert milliseconds into sql timestamp.
* Get the timestamp in UTC because Drill's internal TIMESTAMP stores time in UTC.
*
* @param ms milliseconds
* @return sql timestamp instance
*/
private Timestamp getTimestampWithReplacedZone(long ms) {
return Timestamp.from(Instant.ofEpochMilli(ms)
.atZone(ZoneId.systemDefault())
.withZoneSameLocal(ZoneOffset.UTC)
.toInstant());
}
}
}