/*
* 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.facebook.presto.connector.system.jdbc;
import com.facebook.presto.Session;
import com.facebook.presto.metadata.Metadata;
import com.facebook.presto.metadata.QualifiedTablePrefix;
import com.facebook.presto.security.AccessControl;
import com.facebook.presto.spi.ColumnMetadata;
import com.facebook.presto.spi.ConnectorSession;
import com.facebook.presto.spi.ConnectorTableMetadata;
import com.facebook.presto.spi.InMemoryRecordSet;
import com.facebook.presto.spi.InMemoryRecordSet.Builder;
import com.facebook.presto.spi.RecordCursor;
import com.facebook.presto.spi.SchemaTableName;
import com.facebook.presto.spi.connector.ConnectorTransactionHandle;
import com.facebook.presto.spi.predicate.TupleDomain;
import com.facebook.presto.spi.type.CharType;
import com.facebook.presto.spi.type.DecimalType;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.VarcharType;
import com.facebook.presto.type.ArrayType;
import javax.inject.Inject;
import java.sql.DatabaseMetaData;
import java.sql.Types;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import static com.facebook.presto.connector.system.SystemConnectorSessionUtil.toSession;
import static com.facebook.presto.connector.system.jdbc.FilterUtil.filter;
import static com.facebook.presto.connector.system.jdbc.FilterUtil.stringFilter;
import static com.facebook.presto.metadata.MetadataListing.listCatalogs;
import static com.facebook.presto.metadata.MetadataListing.listTableColumns;
import static com.facebook.presto.metadata.MetadataUtil.TableMetadataBuilder.tableMetadataBuilder;
import static com.facebook.presto.spi.type.BigintType.BIGINT;
import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
import static com.facebook.presto.spi.type.Chars.isCharType;
import static com.facebook.presto.spi.type.DateType.DATE;
import static com.facebook.presto.spi.type.DoubleType.DOUBLE;
import static com.facebook.presto.spi.type.IntegerType.INTEGER;
import static com.facebook.presto.spi.type.RealType.REAL;
import static com.facebook.presto.spi.type.SmallintType.SMALLINT;
import static com.facebook.presto.spi.type.TimeType.TIME;
import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE;
import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP;
import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE;
import static com.facebook.presto.spi.type.TinyintType.TINYINT;
import static com.facebook.presto.spi.type.VarbinaryType.VARBINARY;
import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType;
import static com.facebook.presto.spi.type.Varchars.isVarcharType;
import static java.util.Objects.requireNonNull;
public class ColumnJdbcTable
extends JdbcTable
{
public static final SchemaTableName NAME = new SchemaTableName("jdbc", "columns");
public static final ConnectorTableMetadata METADATA = tableMetadataBuilder(NAME)
.column("table_cat", createUnboundedVarcharType())
.column("table_schem", createUnboundedVarcharType())
.column("table_name", createUnboundedVarcharType())
.column("column_name", createUnboundedVarcharType())
.column("data_type", BIGINT)
.column("type_name", createUnboundedVarcharType())
.column("column_size", BIGINT)
.column("buffer_length", BIGINT)
.column("decimal_digits", BIGINT)
.column("num_prec_radix", BIGINT)
.column("nullable", BIGINT)
.column("remarks", createUnboundedVarcharType())
.column("column_def", createUnboundedVarcharType())
.column("sql_data_type", BIGINT)
.column("sql_datetime_sub", BIGINT)
.column("char_octet_length", BIGINT)
.column("ordinal_position", BIGINT)
.column("is_nullable", createUnboundedVarcharType())
.column("scope_catalog", createUnboundedVarcharType())
.column("scope_schema", createUnboundedVarcharType())
.column("scope_table", createUnboundedVarcharType())
.column("source_data_type", BIGINT)
.column("is_autoincrement", createUnboundedVarcharType())
.column("is_generatedcolumn", createUnboundedVarcharType())
.build();
private final Metadata metadata;
private final AccessControl accessControl;
@Inject
public ColumnJdbcTable(Metadata metadata, AccessControl accessControl)
{
this.metadata = requireNonNull(metadata, "metadata is null");
this.accessControl = requireNonNull(accessControl, "accessControl is null");
}
@Override
public ConnectorTableMetadata getTableMetadata()
{
return METADATA;
}
@Override
public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, ConnectorSession connectorSession, TupleDomain<Integer> constraint)
{
Session session = toSession(transactionHandle, connectorSession);
Optional<String> catalogFilter = stringFilter(constraint, 0);
Optional<String> schemaFilter = stringFilter(constraint, 1);
Optional<String> tableFilter = stringFilter(constraint, 2);
Builder table = InMemoryRecordSet.builder(METADATA);
for (String catalog : filter(listCatalogs(session, metadata, accessControl).keySet(), catalogFilter)) {
QualifiedTablePrefix prefix = FilterUtil.tablePrefix(catalog, schemaFilter, tableFilter);
for (Entry<SchemaTableName, List<ColumnMetadata>> entry : listTableColumns(session, metadata, accessControl, prefix).entrySet()) {
addColumnRows(table, catalog, entry.getKey(), entry.getValue());
}
}
return table.build().cursor();
}
private static void addColumnRows(Builder builder, String catalog, SchemaTableName tableName, List<ColumnMetadata> columns)
{
int ordinalPosition = 1;
for (ColumnMetadata column : columns) {
if (column.isHidden()) {
continue;
}
builder.addRow(
catalog,
tableName.getSchemaName(),
tableName.getTableName(),
column.getName(),
jdbcDataType(column.getType()),
column.getType().getDisplayName(),
columnSize(column.getType()),
0,
decimalDigits(column.getType()),
numPrecRadix(column.getType()),
DatabaseMetaData.columnNullableUnknown,
column.getComment(),
null,
null,
null,
charOctetLength(column.getType()),
ordinalPosition,
"",
null,
null,
null,
null,
null,
null);
ordinalPosition++;
}
}
static int jdbcDataType(Type type)
{
if (type.equals(BOOLEAN)) {
return Types.BOOLEAN;
}
if (type.equals(BIGINT)) {
return Types.BIGINT;
}
if (type.equals(INTEGER)) {
return Types.INTEGER;
}
if (type.equals(SMALLINT)) {
return Types.SMALLINT;
}
if (type.equals(TINYINT)) {
return Types.TINYINT;
}
if (type.equals(REAL)) {
return Types.REAL;
}
if (type.equals(DOUBLE)) {
return Types.DOUBLE;
}
if (type instanceof DecimalType) {
return Types.DECIMAL;
}
if (isVarcharType(type)) {
return Types.LONGNVARCHAR;
}
if (isCharType(type)) {
return Types.CHAR;
}
if (type.equals(VARBINARY)) {
return Types.LONGVARBINARY;
}
if (type.equals(TIME)) {
return Types.TIME;
}
if (type.equals(TIME_WITH_TIME_ZONE)) {
return Types.TIME_WITH_TIMEZONE;
}
if (type.equals(TIMESTAMP)) {
return Types.TIMESTAMP;
}
if (type.equals(TIMESTAMP_WITH_TIME_ZONE)) {
return Types.TIMESTAMP_WITH_TIMEZONE;
}
if (type.equals(DATE)) {
return Types.DATE;
}
if (type instanceof ArrayType) {
return Types.ARRAY;
}
return Types.JAVA_OBJECT;
}
static Integer columnSize(Type type)
{
if (type.equals(BIGINT)) {
return 19; // 2**63-1
}
if (type.equals(INTEGER)) {
return 10; // 2**31-1
}
if (type.equals(SMALLINT)) {
return 5; // 2**15-1
}
if (type.equals(TINYINT)) {
return 3; // 2**7-1
}
if (type instanceof DecimalType) {
return ((DecimalType) type).getPrecision();
}
if (type.equals(REAL)) {
return 24; // IEEE 754
}
if (type.equals(DOUBLE)) {
return 53; // IEEE 754
}
if (isVarcharType(type)) {
return ((VarcharType) type).getLength();
}
if (isCharType(type)) {
return ((CharType) type).getLength();
}
if (type.equals(VARBINARY)) {
return Integer.MAX_VALUE;
}
if (type.equals(TIME)) {
return 8; // 00:00:00
}
if (type.equals(TIME_WITH_TIME_ZONE)) {
return 8 + 6; // 00:00:00+00:00
}
if (type.equals(DATE)) {
return 14; // +5881580-07-11 (2**31-1 days)
}
if (type.equals(TIMESTAMP)) {
return 15 + 8;
}
if (type.equals(TIMESTAMP_WITH_TIME_ZONE)) {
return 15 + 8 + 6;
}
return null;
}
// DECIMAL_DIGITS is the number of fractional digits
private static Integer decimalDigits(Type type)
{
if (type instanceof DecimalType) {
return ((DecimalType) type).getScale();
}
return null;
}
private static Integer charOctetLength(Type type)
{
if (isVarcharType(type)) {
return ((VarcharType) type).getLength();
}
if (isCharType(type)) {
return ((CharType) type).getLength();
}
if (type.equals(VARBINARY)) {
return Integer.MAX_VALUE;
}
return null;
}
static Integer numPrecRadix(Type type)
{
if (type.equals(BIGINT) ||
type.equals(INTEGER) ||
type.equals(SMALLINT) ||
type.equals(TINYINT) ||
(type instanceof DecimalType)) {
return 10;
}
if (type.equals(REAL) || type.equals(DOUBLE)) {
return 2;
}
return null;
}
}