/*
* 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.metadata;
import com.facebook.presto.Session;
import com.facebook.presto.block.BlockEncodingManager;
import com.facebook.presto.connector.ConnectorId;
import com.facebook.presto.spi.CatalogSchemaName;
import com.facebook.presto.spi.ColumnHandle;
import com.facebook.presto.spi.ColumnIdentity;
import com.facebook.presto.spi.ColumnMetadata;
import com.facebook.presto.spi.ConnectorInsertTableHandle;
import com.facebook.presto.spi.ConnectorNewTableLayout;
import com.facebook.presto.spi.ConnectorOutputTableHandle;
import com.facebook.presto.spi.ConnectorResolvedIndex;
import com.facebook.presto.spi.ConnectorSession;
import com.facebook.presto.spi.ConnectorTableHandle;
import com.facebook.presto.spi.ConnectorTableLayout;
import com.facebook.presto.spi.ConnectorTableLayoutResult;
import com.facebook.presto.spi.ConnectorTableMetadata;
import com.facebook.presto.spi.ConnectorViewDefinition;
import com.facebook.presto.spi.Constraint;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.QueryId;
import com.facebook.presto.spi.SchemaTableName;
import com.facebook.presto.spi.SchemaTablePrefix;
import com.facebook.presto.spi.TableIdentity;
import com.facebook.presto.spi.block.BlockEncodingSerde;
import com.facebook.presto.spi.connector.ConnectorMetadata;
import com.facebook.presto.spi.connector.ConnectorOutputMetadata;
import com.facebook.presto.spi.connector.ConnectorTransactionHandle;
import com.facebook.presto.spi.function.OperatorType;
import com.facebook.presto.spi.predicate.TupleDomain;
import com.facebook.presto.spi.security.GrantInfo;
import com.facebook.presto.spi.security.Privilege;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.TypeManager;
import com.facebook.presto.spi.type.TypeSignature;
import com.facebook.presto.sql.analyzer.FeaturesConfig;
import com.facebook.presto.sql.tree.QualifiedName;
import com.facebook.presto.transaction.TransactionManager;
import com.facebook.presto.type.TypeDeserializer;
import com.facebook.presto.type.TypeRegistry;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import io.airlift.json.JsonCodec;
import io.airlift.json.JsonCodecFactory;
import io.airlift.json.ObjectMapperProvider;
import io.airlift.slice.Slice;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.facebook.presto.metadata.QualifiedObjectName.convertFromSchemaTableName;
import static com.facebook.presto.metadata.TableLayout.fromConnectorLayout;
import static com.facebook.presto.metadata.ViewDefinition.ViewColumn;
import static com.facebook.presto.spi.StandardErrorCode.INVALID_VIEW;
import static com.facebook.presto.spi.StandardErrorCode.SYNTAX_ERROR;
import static com.facebook.presto.spi.function.OperatorType.BETWEEN;
import static com.facebook.presto.spi.function.OperatorType.EQUAL;
import static com.facebook.presto.spi.function.OperatorType.GREATER_THAN;
import static com.facebook.presto.spi.function.OperatorType.GREATER_THAN_OR_EQUAL;
import static com.facebook.presto.spi.function.OperatorType.HASH_CODE;
import static com.facebook.presto.spi.function.OperatorType.LESS_THAN;
import static com.facebook.presto.spi.function.OperatorType.LESS_THAN_OR_EQUAL;
import static com.facebook.presto.spi.function.OperatorType.NOT_EQUAL;
import static com.facebook.presto.spi.type.BigintType.BIGINT;
import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
import static com.facebook.presto.transaction.TransactionManager.createTestTransactionManager;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.lang.String.format;
import static java.util.Locale.ENGLISH;
import static java.util.Objects.requireNonNull;
public class MetadataManager
implements Metadata
{
private final FunctionRegistry functions;
private final ProcedureRegistry procedures;
private final TypeManager typeManager;
private final JsonCodec<ViewDefinition> viewCodec;
private final BlockEncodingSerde blockEncodingSerde;
private final SessionPropertyManager sessionPropertyManager;
private final SchemaPropertyManager schemaPropertyManager;
private final TablePropertyManager tablePropertyManager;
private final TransactionManager transactionManager;
private final ConcurrentMap<String, Collection<ConnectorMetadata>> catalogsByQueryId = new ConcurrentHashMap<>();
public MetadataManager(FeaturesConfig featuresConfig,
TypeManager typeManager,
BlockEncodingSerde blockEncodingSerde,
SessionPropertyManager sessionPropertyManager,
SchemaPropertyManager schemaPropertyManager,
TablePropertyManager tablePropertyManager,
TransactionManager transactionManager)
{
this(featuresConfig,
typeManager,
createTestingViewCodec(),
blockEncodingSerde,
sessionPropertyManager,
schemaPropertyManager,
tablePropertyManager,
transactionManager);
}
@Inject
public MetadataManager(FeaturesConfig featuresConfig,
TypeManager typeManager,
JsonCodec<ViewDefinition> viewCodec,
BlockEncodingSerde blockEncodingSerde,
SessionPropertyManager sessionPropertyManager,
SchemaPropertyManager schemaPropertyManager,
TablePropertyManager tablePropertyManager,
TransactionManager transactionManager)
{
functions = new FunctionRegistry(typeManager, blockEncodingSerde, featuresConfig);
procedures = new ProcedureRegistry(typeManager);
this.typeManager = requireNonNull(typeManager, "types is null");
this.viewCodec = requireNonNull(viewCodec, "viewCodec is null");
this.blockEncodingSerde = requireNonNull(blockEncodingSerde, "blockEncodingSerde is null");
this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null");
this.schemaPropertyManager = requireNonNull(schemaPropertyManager, "schemaPropertyManager is null");
this.tablePropertyManager = requireNonNull(tablePropertyManager, "tablePropertyManager is null");
this.transactionManager = requireNonNull(transactionManager, "transactionManager is null");
verifyComparableOrderableContract();
}
public static MetadataManager createTestMetadataManager()
{
return createTestMetadataManager(new CatalogManager());
}
public static MetadataManager createTestMetadataManager(CatalogManager catalogManager)
{
TypeManager typeManager = new TypeRegistry();
return new MetadataManager(
new FeaturesConfig(),
typeManager,
new BlockEncodingManager(typeManager),
new SessionPropertyManager(),
new SchemaPropertyManager(),
new TablePropertyManager(),
createTestTransactionManager(catalogManager));
}
@Override
public final void verifyComparableOrderableContract()
{
Multimap<Type, OperatorType> missingOperators = HashMultimap.create();
for (Type type : typeManager.getTypes()) {
if (type.isComparable()) {
if (!functions.canResolveOperator(HASH_CODE, BIGINT, ImmutableList.of(type))) {
missingOperators.put(type, HASH_CODE);
}
if (!functions.canResolveOperator(EQUAL, BOOLEAN, ImmutableList.of(type, type))) {
missingOperators.put(type, EQUAL);
}
if (!functions.canResolveOperator(NOT_EQUAL, BOOLEAN, ImmutableList.of(type, type))) {
missingOperators.put(type, NOT_EQUAL);
}
}
if (type.isOrderable()) {
for (OperatorType operator : ImmutableList.of(LESS_THAN, LESS_THAN_OR_EQUAL, GREATER_THAN, GREATER_THAN_OR_EQUAL)) {
if (!functions.canResolveOperator(operator, BOOLEAN, ImmutableList.of(type, type))) {
missingOperators.put(type, operator);
}
}
if (!functions.canResolveOperator(BETWEEN, BOOLEAN, ImmutableList.of(type, type, type))) {
missingOperators.put(type, BETWEEN);
}
}
}
// TODO: verify the parametric types too
if (!missingOperators.isEmpty()) {
List<String> messages = new ArrayList<>();
for (Type type : missingOperators.keySet()) {
messages.add(format("%s missing for %s", missingOperators.get(type), type));
}
throw new IllegalStateException(Joiner.on(", ").join(messages));
}
}
@Override
public Type getType(TypeSignature signature)
{
return typeManager.getType(signature);
}
@Override
public boolean isAggregationFunction(QualifiedName name)
{
// TODO: transactional when FunctionRegistry is made transactional
return functions.isAggregationFunction(name);
}
@Override
public List<SqlFunction> listFunctions()
{
// TODO: transactional when FunctionRegistry is made transactional
return functions.list();
}
@Override
public void addFunctions(List<? extends SqlFunction> functionInfos)
{
// TODO: transactional when FunctionRegistry is made transactional
functions.addFunctions(functionInfos);
}
@Override
public boolean schemaExists(Session session, CatalogSchemaName schema)
{
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, schema.getCatalogName());
if (!catalog.isPresent()) {
return false;
}
CatalogMetadata catalogMetadata = catalog.get();
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getConnectorId());
return catalogMetadata.listConnectorIds().stream()
.map(catalogMetadata::getMetadataFor)
.anyMatch(metadata -> metadata.schemaExists(connectorSession, schema.getSchemaName()));
}
@Override
public List<String> listSchemaNames(Session session, String catalogName)
{
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, catalogName);
ImmutableSet.Builder<String> schemaNames = ImmutableSet.builder();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getConnectorId());
for (ConnectorId connectorId : catalogMetadata.listConnectorIds()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
schemaNames.addAll(metadata.listSchemaNames(connectorSession));
}
}
return ImmutableList.copyOf(schemaNames.build());
}
@Override
public Optional<TableHandle> getTableHandle(Session session, QualifiedObjectName table)
{
requireNonNull(table, "table is null");
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, table.getCatalogName());
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
ConnectorId connectorId = catalogMetadata.getConnectorId(table);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorTableHandle tableHandle = metadata.getTableHandle(session.toConnectorSession(connectorId), table.asSchemaTableName());
if (tableHandle != null) {
return Optional.of(new TableHandle(connectorId, tableHandle));
}
}
return Optional.empty();
}
@Override
public List<TableLayoutResult> getLayouts(Session session, TableHandle table, Constraint<ColumnHandle> constraint, Optional<Set<ColumnHandle>> desiredColumns)
{
if (constraint.getSummary().isNone()) {
return ImmutableList.of();
}
ConnectorId connectorId = table.getConnectorId();
ConnectorTableHandle connectorTable = table.getConnectorHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, connectorId);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorTransactionHandle transaction = catalogMetadata.getTransactionHandleFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
List<ConnectorTableLayoutResult> layouts = metadata.getTableLayouts(connectorSession, connectorTable, constraint, desiredColumns);
return layouts.stream()
.map(layout -> new TableLayoutResult(fromConnectorLayout(connectorId, transaction, layout.getTableLayout()), layout.getUnenforcedConstraint()))
.collect(toImmutableList());
}
@Override
public TableLayout getLayout(Session session, TableLayoutHandle handle)
{
ConnectorId connectorId = handle.getConnectorId();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, connectorId);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorTransactionHandle transaction = catalogMetadata.getTransactionHandleFor(connectorId);
return fromConnectorLayout(connectorId, transaction, metadata.getTableLayout(session.toConnectorSession(connectorId), handle.getConnectorHandle()));
}
@Override
public Optional<Object> getInfo(Session session, TableLayoutHandle handle)
{
ConnectorId connectorId = handle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
ConnectorTableLayout tableLayout = metadata.getTableLayout(session.toConnectorSession(connectorId), handle.getConnectorHandle());
return metadata.getInfo(tableLayout.getHandle());
}
@Override
public TableMetadata getTableMetadata(Session session, TableHandle tableHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
ConnectorTableMetadata tableMetadata = metadata.getTableMetadata(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle());
return new TableMetadata(connectorId, tableMetadata);
}
@Override
public Map<String, ColumnHandle> getColumnHandles(Session session, TableHandle tableHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
Map<String, ColumnHandle> handles = metadata.getColumnHandles(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle());
ImmutableMap.Builder<String, ColumnHandle> map = ImmutableMap.builder();
for (Entry<String, ColumnHandle> mapEntry : handles.entrySet()) {
map.put(mapEntry.getKey().toLowerCase(ENGLISH), mapEntry.getValue());
}
return map.build();
}
@Override
public ColumnMetadata getColumnMetadata(Session session, TableHandle tableHandle, ColumnHandle columnHandle)
{
requireNonNull(tableHandle, "tableHandle is null");
requireNonNull(columnHandle, "columnHandle is null");
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
return metadata.getColumnMetadata(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), columnHandle);
}
@Override
public List<QualifiedObjectName> listTables(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Set<QualifiedObjectName> tables = new LinkedHashSet<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
String schemaNameOrNull = prefix.getSchemaName().orElse(null);
for (ConnectorId connectorId : catalogMetadata.listConnectorIds()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
metadata.listTables(connectorSession, schemaNameOrNull).stream()
.map(convertFromSchemaTableName(prefix.getCatalogName())::apply)
.forEach(tables::add);
}
}
return ImmutableList.copyOf(tables);
}
@Override
public Map<QualifiedObjectName, List<ColumnMetadata>> listTableColumns(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Map<QualifiedObjectName, List<ColumnMetadata>> tableColumns = new HashMap<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
SchemaTablePrefix tablePrefix = prefix.asSchemaTablePrefix();
for (ConnectorId connectorId : catalogMetadata.listConnectorIds()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
for (Entry<SchemaTableName, List<ColumnMetadata>> entry : metadata.listTableColumns(connectorSession, tablePrefix).entrySet()) {
QualifiedObjectName tableName = new QualifiedObjectName(
prefix.getCatalogName(),
entry.getKey().getSchemaName(),
entry.getKey().getTableName());
tableColumns.put(tableName, entry.getValue());
}
// if table and view names overlap, the view wins
for (Entry<SchemaTableName, ConnectorViewDefinition> entry : metadata.getViews(connectorSession, tablePrefix).entrySet()) {
QualifiedObjectName tableName = new QualifiedObjectName(
prefix.getCatalogName(),
entry.getKey().getSchemaName(),
entry.getKey().getTableName());
ImmutableList.Builder<ColumnMetadata> columns = ImmutableList.builder();
for (ViewColumn column : deserializeView(entry.getValue().getViewData()).getColumns()) {
columns.add(new ColumnMetadata(column.getName(), column.getType()));
}
tableColumns.put(tableName, columns.build());
}
}
}
return ImmutableMap.copyOf(tableColumns);
}
@Override
public void createSchema(Session session, CatalogSchemaName schema, Map<String, Object> properties)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schema.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.createSchema(session.toConnectorSession(connectorId), schema.getSchemaName(), properties);
}
@Override
public void dropSchema(Session session, CatalogSchemaName schema)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schema.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.dropSchema(session.toConnectorSession(connectorId), schema.getSchemaName());
}
@Override
public void renameSchema(Session session, CatalogSchemaName source, String target)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, source.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.renameSchema(session.toConnectorSession(connectorId), source.getSchemaName(), target);
}
@Override
public TableIdentity getTableIdentity(Session session, TableHandle tableHandle)
{
ConnectorMetadata metadata = getMetadata(session, tableHandle.getConnectorId());
return metadata.getTableIdentity(tableHandle.getConnectorHandle());
}
@Override
public TableIdentity deserializeTableIdentity(Session session, String catalogName, byte[] bytes)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
return catalogMetadata.getMetadata().deserializeTableIdentity(bytes);
}
@Override
public ColumnIdentity getColumnIdentity(Session session, TableHandle tableHandle, ColumnHandle columnHandle)
{
ConnectorMetadata metadata = getMetadata(session, tableHandle.getConnectorId());
return metadata.getColumnIdentity(columnHandle);
}
@Override
public ColumnIdentity deserializeColumnIdentity(Session session, String catalogName, byte[] bytes)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
return catalogMetadata.getMetadata().deserializeColumnIdentity(bytes);
}
@Override
public void createTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.createTable(session.toConnectorSession(connectorId), tableMetadata);
}
@Override
public void renameTable(Session session, TableHandle tableHandle, QualifiedObjectName newTableName)
{
String catalogName = newTableName.getCatalogName();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
ConnectorId connectorId = catalogMetadata.getConnectorId();
if (!tableHandle.getConnectorId().equals(connectorId)) {
throw new PrestoException(SYNTAX_ERROR, "Cannot rename tables across catalogs");
}
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.renameTable(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), newTableName.asSchemaTableName());
}
@Override
public void renameColumn(Session session, TableHandle tableHandle, ColumnHandle source, String target)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadataForWrite(session, connectorId);
metadata.renameColumn(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), source, target.toLowerCase(ENGLISH));
}
@Override
public void addColumn(Session session, TableHandle tableHandle, ColumnMetadata column)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadataForWrite(session, connectorId);
metadata.addColumn(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), column);
}
@Override
public void dropTable(Session session, TableHandle tableHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadataForWrite(session, connectorId);
metadata.dropTable(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle());
}
@Override
public Optional<NewTableLayout> getInsertLayout(Session session, TableHandle table)
{
ConnectorId connectorId = table.getConnectorId();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, connectorId);
ConnectorMetadata metadata = catalogMetadata.getMetadata();
Optional<ConnectorNewTableLayout> insertLayout = metadata.getInsertLayout(session.toConnectorSession(connectorId), table.getConnectorHandle());
if (!insertLayout.isPresent()) {
return Optional.empty();
}
return Optional.of(new NewTableLayout(connectorId, catalogMetadata.getTransactionHandleFor(connectorId), insertLayout.get()));
}
@Override
public Optional<NewTableLayout> getNewTableLayout(Session session, String catalogName, ConnectorTableMetadata tableMetadata)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
return metadata.getNewTableLayout(connectorSession, tableMetadata)
.map(layout -> new NewTableLayout(connectorId, transactionHandle, layout));
}
@Override
public void beginQuery(Session session, Set<ConnectorId> connectors)
{
for (ConnectorId connectorId : connectors) {
ConnectorMetadata metadata = getMetadata(session, connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
metadata.beginQuery(connectorSession);
registerCatalogForQueryId(session.getQueryId(), metadata);
}
}
private void registerCatalogForQueryId(QueryId queryId, ConnectorMetadata metadata)
{
catalogsByQueryId.putIfAbsent(queryId.getId(), new ArrayList<>());
catalogsByQueryId.get(queryId.getId()).add(metadata);
}
@Override
public void cleanupQuery(Session session)
{
try {
Collection<ConnectorMetadata> catalogs = catalogsByQueryId.get(session.getQueryId().getId());
if (catalogs == null) {
return;
}
for (ConnectorMetadata metadata : catalogs) {
metadata.cleanupQuery(session.toConnectorSession());
}
}
finally {
catalogsByQueryId.remove(session.getQueryId().getId());
}
}
@Override
public OutputTableHandle beginCreateTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata, Optional<NewTableLayout> layout)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
ConnectorOutputTableHandle handle = metadata.beginCreateTable(connectorSession, tableMetadata, layout.map(NewTableLayout::getLayout));
return new OutputTableHandle(connectorId, transactionHandle, handle);
}
@Override
public Optional<ConnectorOutputMetadata> finishCreateTable(Session session, OutputTableHandle tableHandle, Collection<Slice> fragments)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
return metadata.finishCreateTable(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), fragments);
}
@Override
public InsertTableHandle beginInsert(Session session, TableHandle tableHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, connectorId);
ConnectorMetadata metadata = catalogMetadata.getMetadata();
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(connectorId);
ConnectorInsertTableHandle handle = metadata.beginInsert(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle());
return new InsertTableHandle(tableHandle.getConnectorId(), transactionHandle, handle);
}
@Override
public Optional<ConnectorOutputMetadata> finishInsert(Session session, InsertTableHandle tableHandle, Collection<Slice> fragments)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
return metadata.finishInsert(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), fragments);
}
@Override
public ColumnHandle getUpdateRowIdColumnHandle(Session session, TableHandle tableHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
return metadata.getUpdateRowIdColumnHandle(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle());
}
@Override
public boolean supportsMetadataDelete(Session session, TableHandle tableHandle, TableLayoutHandle tableLayoutHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
return metadata.supportsMetadataDelete(
session.toConnectorSession(connectorId),
tableHandle.getConnectorHandle(),
tableLayoutHandle.getConnectorHandle());
}
@Override
public OptionalLong metadataDelete(Session session, TableHandle tableHandle, TableLayoutHandle tableLayoutHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadataForWrite(session, connectorId);
return metadata.metadataDelete(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), tableLayoutHandle.getConnectorHandle());
}
@Override
public TableHandle beginDelete(Session session, TableHandle tableHandle)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadataForWrite(session, connectorId);
ConnectorTableHandle newHandle = metadata.beginDelete(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle());
return new TableHandle(tableHandle.getConnectorId(), newHandle);
}
@Override
public void finishDelete(Session session, TableHandle tableHandle, Collection<Slice> fragments)
{
ConnectorId connectorId = tableHandle.getConnectorId();
ConnectorMetadata metadata = getMetadata(session, connectorId);
metadata.finishDelete(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle(), fragments);
}
@Override
public Optional<ConnectorId> getCatalogHandle(Session session, String catalogName)
{
return transactionManager.getOptionalCatalogMetadata(session.getRequiredTransactionId(), catalogName).map(CatalogMetadata::getConnectorId);
}
@Override
public Map<String, ConnectorId> getCatalogNames(Session session)
{
return transactionManager.getCatalogNames(session.getRequiredTransactionId());
}
@Override
public List<QualifiedObjectName> listViews(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Set<QualifiedObjectName> views = new LinkedHashSet<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
String schemaNameOrNull = prefix.getSchemaName().orElse(null);
for (ConnectorId connectorId : catalogMetadata.listConnectorIds()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
metadata.listViews(connectorSession, schemaNameOrNull).stream()
.map(convertFromSchemaTableName(prefix.getCatalogName())::apply)
.forEach(views::add);
}
}
return ImmutableList.copyOf(views);
}
@Override
public Map<QualifiedObjectName, ViewDefinition> getViews(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Map<QualifiedObjectName, ViewDefinition> views = new LinkedHashMap<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
SchemaTablePrefix tablePrefix = prefix.asSchemaTablePrefix();
for (ConnectorId connectorId : catalogMetadata.listConnectorIds()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
for (Entry<SchemaTableName, ConnectorViewDefinition> entry : metadata.getViews(connectorSession, tablePrefix).entrySet()) {
QualifiedObjectName viewName = new QualifiedObjectName(
prefix.getCatalogName(),
entry.getKey().getSchemaName(),
entry.getKey().getTableName());
views.put(viewName, deserializeView(entry.getValue().getViewData()));
}
}
}
return ImmutableMap.copyOf(views);
}
@Override
public Optional<ViewDefinition> getView(Session session, QualifiedObjectName viewName)
{
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, viewName.getCatalogName());
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
ConnectorId connectorId = catalogMetadata.getConnectorId(viewName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
Map<SchemaTableName, ConnectorViewDefinition> views = metadata.getViews(
session.toConnectorSession(connectorId),
viewName.asSchemaTableName().toSchemaTablePrefix());
ConnectorViewDefinition view = views.get(viewName.asSchemaTableName());
if (view != null) {
return Optional.of(deserializeView(view.getViewData()));
}
}
return Optional.empty();
}
@Override
public void createView(Session session, QualifiedObjectName viewName, String viewData, boolean replace)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.createView(session.toConnectorSession(connectorId), viewName.asSchemaTableName(), viewData, replace);
}
@Override
public void dropView(Session session, QualifiedObjectName viewName)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.dropView(session.toConnectorSession(connectorId), viewName.asSchemaTableName());
}
@Override
public Optional<ResolvedIndex> resolveIndex(Session session, TableHandle tableHandle, Set<ColumnHandle> indexableColumns, Set<ColumnHandle> outputColumns, TupleDomain<ColumnHandle> tupleDomain)
{
ConnectorId connectorId = tableHandle.getConnectorId();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, connectorId);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
ConnectorTransactionHandle transaction = catalogMetadata.getTransactionHandleFor(connectorId);
ConnectorSession connectorSession = session.toConnectorSession(connectorId);
Optional<ConnectorResolvedIndex> resolvedIndex = metadata.resolveIndex(connectorSession, tableHandle.getConnectorHandle(), indexableColumns, outputColumns, tupleDomain);
return resolvedIndex.map(resolved -> new ResolvedIndex(tableHandle.getConnectorId(), transaction, resolved));
}
@Override
public void grantTablePrivileges(Session session, QualifiedObjectName tableName, Set<Privilege> privileges, String grantee, boolean grantOption)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, tableName.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.grantTablePrivileges(session.toConnectorSession(connectorId), tableName.asSchemaTableName(), privileges, grantee, grantOption);
}
@Override
public void revokeTablePrivileges(Session session, QualifiedObjectName tableName, Set<Privilege> privileges, String grantee, boolean grantOption)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, tableName.getCatalogName());
ConnectorId connectorId = catalogMetadata.getConnectorId();
ConnectorMetadata metadata = catalogMetadata.getMetadata();
metadata.revokeTablePrivileges(session.toConnectorSession(connectorId), tableName.asSchemaTableName(), privileges, grantee, grantOption);
}
@Override
public List<GrantInfo> listTablePrivileges(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
SchemaTablePrefix tablePrefix = prefix.asSchemaTablePrefix();
Optional<CatalogMetadata> catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
ImmutableSet.Builder<GrantInfo> grantInfos = ImmutableSet.builder();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getConnectorId());
for (ConnectorId connectorId : catalogMetadata.listConnectorIds()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId);
grantInfos.addAll(metadata.listTablePrivileges(connectorSession, tablePrefix));
}
}
return ImmutableList.copyOf(grantInfos.build());
}
@Override
public FunctionRegistry getFunctionRegistry()
{
// TODO: transactional when FunctionRegistry is made transactional
return functions;
}
@Override
public ProcedureRegistry getProcedureRegistry()
{
return procedures;
}
@Override
public TypeManager getTypeManager()
{
// TODO: make this transactional when we allow user defined types
return typeManager;
}
@Override
public BlockEncodingSerde getBlockEncodingSerde()
{
return blockEncodingSerde;
}
@Override
public SessionPropertyManager getSessionPropertyManager()
{
return sessionPropertyManager;
}
@Override
public SchemaPropertyManager getSchemaPropertyManager()
{
return schemaPropertyManager;
}
@Override
public TablePropertyManager getTablePropertyManager()
{
return tablePropertyManager;
}
private ViewDefinition deserializeView(String data)
{
try {
return viewCodec.fromJson(data);
}
catch (IllegalArgumentException e) {
throw new PrestoException(INVALID_VIEW, "Invalid view JSON: " + data, e);
}
}
private Optional<CatalogMetadata> getOptionalCatalogMetadata(Session session, String catalogName)
{
return transactionManager.getOptionalCatalogMetadata(session.getRequiredTransactionId(), catalogName);
}
private CatalogMetadata getCatalogMetadata(Session session, ConnectorId connectorId)
{
return transactionManager.getCatalogMetadata(session.getRequiredTransactionId(), connectorId);
}
private CatalogMetadata getCatalogMetadataForWrite(Session session, String catalogName)
{
return transactionManager.getCatalogMetadataForWrite(session.getRequiredTransactionId(), catalogName);
}
private CatalogMetadata getCatalogMetadataForWrite(Session session, ConnectorId connectorId)
{
return transactionManager.getCatalogMetadataForWrite(session.getRequiredTransactionId(), connectorId);
}
private ConnectorMetadata getMetadata(Session session, ConnectorId connectorId)
{
return getCatalogMetadata(session, connectorId).getMetadataFor(connectorId);
}
private ConnectorMetadata getMetadataForWrite(Session session, ConnectorId connectorId)
{
return getCatalogMetadataForWrite(session, connectorId).getMetadata();
}
private static JsonCodec<ViewDefinition> createTestingViewCodec()
{
ObjectMapperProvider provider = new ObjectMapperProvider();
provider.setJsonDeserializers(ImmutableMap.of(Type.class, new TypeDeserializer(new TypeRegistry())));
return new JsonCodecFactory(provider).jsonCodec(ViewDefinition.class);
}
@VisibleForTesting
public Map<String, Collection<ConnectorMetadata>> getCatalogsByQueryId()
{
return ImmutableMap.copyOf(catalogsByQueryId);
}
}