Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.trino.metadata.MetadataManager Maven / Gradle / Ivy
/*
* 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 io.trino.metadata;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import com.google.inject.Inject;
import io.airlift.log.Logger;
import io.airlift.slice.Slice;
import io.trino.FeaturesConfig;
import io.trino.Session;
import io.trino.connector.system.GlobalSystemConnector;
import io.trino.metadata.LanguageFunctionManager.RunAsIdentityLoader;
import io.trino.metadata.ResolvedFunction.ResolvedFunctionDecoder;
import io.trino.spi.ErrorCode;
import io.trino.spi.QueryId;
import io.trino.spi.TrinoException;
import io.trino.spi.connector.AggregateFunction;
import io.trino.spi.connector.AggregationApplicationResult;
import io.trino.spi.connector.Assignment;
import io.trino.spi.connector.BeginTableExecuteResult;
import io.trino.spi.connector.CatalogHandle;
import io.trino.spi.connector.CatalogSchemaName;
import io.trino.spi.connector.CatalogSchemaTableName;
import io.trino.spi.connector.ColumnHandle;
import io.trino.spi.connector.ColumnMetadata;
import io.trino.spi.connector.ConnectorAnalyzeMetadata;
import io.trino.spi.connector.ConnectorCapabilities;
import io.trino.spi.connector.ConnectorInsertTableHandle;
import io.trino.spi.connector.ConnectorMaterializedViewDefinition;
import io.trino.spi.connector.ConnectorMergeTableHandle;
import io.trino.spi.connector.ConnectorMetadata;
import io.trino.spi.connector.ConnectorOutputMetadata;
import io.trino.spi.connector.ConnectorOutputTableHandle;
import io.trino.spi.connector.ConnectorPartitioningHandle;
import io.trino.spi.connector.ConnectorResolvedIndex;
import io.trino.spi.connector.ConnectorSession;
import io.trino.spi.connector.ConnectorTableExecuteHandle;
import io.trino.spi.connector.ConnectorTableHandle;
import io.trino.spi.connector.ConnectorTableMetadata;
import io.trino.spi.connector.ConnectorTableSchema;
import io.trino.spi.connector.ConnectorTableVersion;
import io.trino.spi.connector.ConnectorTransactionHandle;
import io.trino.spi.connector.ConnectorViewDefinition;
import io.trino.spi.connector.Constraint;
import io.trino.spi.connector.ConstraintApplicationResult;
import io.trino.spi.connector.JoinApplicationResult;
import io.trino.spi.connector.JoinStatistics;
import io.trino.spi.connector.JoinType;
import io.trino.spi.connector.LimitApplicationResult;
import io.trino.spi.connector.MaterializedViewFreshness;
import io.trino.spi.connector.ProjectionApplicationResult;
import io.trino.spi.connector.RelationColumnsMetadata;
import io.trino.spi.connector.RelationCommentMetadata;
import io.trino.spi.connector.RowChangeParadigm;
import io.trino.spi.connector.SampleApplicationResult;
import io.trino.spi.connector.SampleType;
import io.trino.spi.connector.SaveMode;
import io.trino.spi.connector.SchemaTableName;
import io.trino.spi.connector.SchemaTablePrefix;
import io.trino.spi.connector.SortItem;
import io.trino.spi.connector.SystemTable;
import io.trino.spi.connector.TableColumnsMetadata;
import io.trino.spi.connector.TableFunctionApplicationResult;
import io.trino.spi.connector.TableScanRedirectApplicationResult;
import io.trino.spi.connector.TopNApplicationResult;
import io.trino.spi.connector.WriterScalingOptions;
import io.trino.spi.expression.ConnectorExpression;
import io.trino.spi.expression.Constant;
import io.trino.spi.expression.Variable;
import io.trino.spi.function.AggregationFunctionMetadata;
import io.trino.spi.function.AggregationFunctionMetadata.AggregationFunctionMetadataBuilder;
import io.trino.spi.function.BoundSignature;
import io.trino.spi.function.CatalogSchemaFunctionName;
import io.trino.spi.function.FunctionDependencyDeclaration;
import io.trino.spi.function.FunctionId;
import io.trino.spi.function.FunctionMetadata;
import io.trino.spi.function.LanguageFunction;
import io.trino.spi.function.OperatorType;
import io.trino.spi.function.SchemaFunctionName;
import io.trino.spi.function.Signature;
import io.trino.spi.predicate.TupleDomain;
import io.trino.spi.security.GrantInfo;
import io.trino.spi.security.Identity;
import io.trino.spi.security.Privilege;
import io.trino.spi.security.RoleGrant;
import io.trino.spi.security.TrinoPrincipal;
import io.trino.spi.statistics.ComputedStatistics;
import io.trino.spi.statistics.TableStatistics;
import io.trino.spi.statistics.TableStatisticsMetadata;
import io.trino.spi.type.Type;
import io.trino.spi.type.TypeManager;
import io.trino.spi.type.TypeNotFoundException;
import io.trino.spi.type.TypeOperators;
import io.trino.sql.analyzer.TypeSignatureProvider;
import io.trino.sql.parser.SqlParser;
import io.trino.sql.planner.ConnectorExpressions;
import io.trino.sql.planner.PartitioningHandle;
import io.trino.sql.tree.QualifiedName;
import io.trino.transaction.TransactionManager;
import io.trino.type.BlockTypeOperators;
import io.trino.type.TypeCoercion;
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.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.base.Verify.verifyNotNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Streams.stream;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static io.airlift.concurrent.MoreFutures.toListenableFuture;
import static io.trino.SystemSessionProperties.getRetryPolicy;
import static io.trino.client.NodeVersion.UNKNOWN;
import static io.trino.metadata.CatalogMetadata.SecurityManagement.CONNECTOR;
import static io.trino.metadata.CatalogMetadata.SecurityManagement.SYSTEM;
import static io.trino.metadata.GlobalFunctionCatalog.BUILTIN_SCHEMA;
import static io.trino.metadata.GlobalFunctionCatalog.isBuiltinFunctionName;
import static io.trino.metadata.LanguageFunctionManager.isTrinoSqlLanguageFunction;
import static io.trino.metadata.QualifiedObjectName.convertFromSchemaTableName;
import static io.trino.metadata.RedirectionAwareTableHandle.noRedirection;
import static io.trino.metadata.RedirectionAwareTableHandle.withRedirectionTo;
import static io.trino.metadata.SignatureBinder.applyBoundVariables;
import static io.trino.spi.ErrorType.EXTERNAL;
import static io.trino.spi.StandardErrorCode.FUNCTION_IMPLEMENTATION_ERROR;
import static io.trino.spi.StandardErrorCode.FUNCTION_IMPLEMENTATION_MISSING;
import static io.trino.spi.StandardErrorCode.INVALID_VIEW;
import static io.trino.spi.StandardErrorCode.NOT_FOUND;
import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED;
import static io.trino.spi.StandardErrorCode.SCHEMA_NOT_FOUND;
import static io.trino.spi.StandardErrorCode.SYNTAX_ERROR;
import static io.trino.spi.StandardErrorCode.TABLE_REDIRECTION_ERROR;
import static io.trino.spi.StandardErrorCode.UNSUPPORTED_TABLE_TYPE;
import static io.trino.spi.connector.MaterializedViewFreshness.Freshness.STALE;
import static io.trino.transaction.InMemoryTransactionManager.createTestTransactionManager;
import static io.trino.type.InternalTypeManager.TESTING_TYPE_MANAGER;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static java.util.Locale.ENGLISH;
import static java.util.Objects.requireNonNull;
public final class MetadataManager
implements Metadata
{
private static final Logger log = Logger.get(MetadataManager.class);
@VisibleForTesting
public static final int MAX_TABLE_REDIRECTIONS = 10;
private final GlobalFunctionCatalog functions;
private final BuiltinFunctionResolver functionResolver;
private final SystemSecurityMetadata systemSecurityMetadata;
private final TransactionManager transactionManager;
private final LanguageFunctionManager languageFunctionManager;
private final TypeManager typeManager;
private final TypeCoercion typeCoercion;
private final ConcurrentMap catalogsByQueryId = new ConcurrentHashMap<>();
private final ResolvedFunctionDecoder functionDecoder;
@Inject
public MetadataManager(
SystemSecurityMetadata systemSecurityMetadata,
TransactionManager transactionManager,
GlobalFunctionCatalog globalFunctionCatalog,
LanguageFunctionManager languageFunctionManager,
TypeManager typeManager)
{
this.typeManager = requireNonNull(typeManager, "typeManager is null");
functions = requireNonNull(globalFunctionCatalog, "globalFunctionCatalog is null");
functionDecoder = new ResolvedFunctionDecoder(typeManager::getType);
functionResolver = new BuiltinFunctionResolver(this, typeManager, globalFunctionCatalog, functionDecoder);
this.typeCoercion = new TypeCoercion(typeManager::getType);
this.systemSecurityMetadata = requireNonNull(systemSecurityMetadata, "systemSecurityMetadata is null");
this.transactionManager = requireNonNull(transactionManager, "transactionManager is null");
this.languageFunctionManager = requireNonNull(languageFunctionManager, "languageFunctionManager is null");
}
@Override
public Set getConnectorCapabilities(Session session, CatalogHandle catalogHandle)
{
return getCatalogMetadata(session, catalogHandle).getConnectorCapabilities();
}
@Override
public boolean catalogExists(Session session, String catalogName)
{
return getOptionalCatalogMetadata(session, catalogName).isPresent();
}
@Override
public boolean schemaExists(Session session, CatalogSchemaName schema)
{
Optional catalog = getOptionalCatalogMetadata(session, schema.getCatalogName());
if (catalog.isEmpty()) {
return false;
}
CatalogMetadata catalogMetadata = catalog.get();
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
return catalogMetadata.listCatalogHandles().stream()
.map(catalogName -> catalogMetadata.getMetadataFor(session, catalogName))
.anyMatch(metadata -> metadata.schemaExists(connectorSession, schema.getSchemaName()));
}
@Override
public List listSchemaNames(Session session, String catalogName)
{
Optional catalog = getOptionalCatalogMetadata(session, catalogName);
ImmutableSet.Builder schemaNames = ImmutableSet.builder();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
metadata.listSchemaNames(connectorSession).stream()
.map(schema -> schema.toLowerCase(Locale.ENGLISH))
.filter(schema -> !isExternalInformationSchema(catalogHandle, schema))
.forEach(schemaNames::add);
}
}
return ImmutableList.copyOf(schemaNames.build());
}
@Override
public Optional getTableHandle(Session session, QualifiedObjectName table)
{
return getTableHandle(session, table, Optional.empty(), Optional.empty());
}
@Override
public Optional getTableHandle(Session session, QualifiedObjectName table, Optional startVersion, Optional endVersion)
{
requireNonNull(table, "table is null");
if (table.getCatalogName().isEmpty() || table.getSchemaName().isEmpty() || table.getObjectName().isEmpty()) {
// Table cannot exist
return Optional.empty();
}
return getOptionalCatalogMetadata(session, table.getCatalogName()).flatMap(catalogMetadata -> {
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(session, table);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ConnectorTableHandle tableHandle = metadata.getTableHandle(
connectorSession,
table.asSchemaTableName(),
toConnectorVersion(startVersion),
toConnectorVersion(endVersion));
return Optional.ofNullable(tableHandle)
.map(connectorTableHandle -> new TableHandle(
catalogHandle,
connectorTableHandle,
catalogMetadata.getTransactionHandleFor(catalogHandle)));
});
}
@Override
public Optional getTableHandleForExecute(Session session, TableHandle tableHandle, String procedure, Map executeProperties)
{
requireNonNull(session, "session is null");
requireNonNull(tableHandle, "tableHandle is null");
requireNonNull(procedure, "procedure is null");
requireNonNull(executeProperties, "executeProperties is null");
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
Optional executeHandle = metadata.getTableHandleForExecute(
session.toConnectorSession(catalogHandle),
tableHandle.getConnectorHandle(),
procedure,
executeProperties,
getRetryPolicy(session).getRetryMode());
return executeHandle.map(handle -> new TableExecuteHandle(
catalogHandle,
tableHandle.getTransaction(),
handle));
}
@Override
public Optional getLayoutForTableExecute(Session session, TableExecuteHandle tableExecuteHandle)
{
CatalogHandle catalogHandle = tableExecuteHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return metadata.getLayoutForTableExecute(session.toConnectorSession(catalogHandle), tableExecuteHandle.getConnectorHandle())
.map(layout -> new TableLayout(catalogHandle, catalogMetadata.getTransactionHandleFor(catalogHandle), layout));
}
@Override
public BeginTableExecuteResult beginTableExecute(Session session, TableExecuteHandle tableExecuteHandle, TableHandle sourceHandle)
{
CatalogHandle catalogHandle = tableExecuteHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
BeginTableExecuteResult connectorBeginResult = metadata.beginTableExecute(session.toConnectorSession(), tableExecuteHandle.getConnectorHandle(), sourceHandle.getConnectorHandle());
return new BeginTableExecuteResult<>(
tableExecuteHandle.withConnectorHandle(connectorBeginResult.getTableExecuteHandle()),
sourceHandle.withConnectorHandle(connectorBeginResult.getSourceHandle()));
}
@Override
public void finishTableExecute(Session session, TableExecuteHandle tableExecuteHandle, Collection fragments, List tableExecuteState)
{
CatalogHandle catalogHandle = tableExecuteHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
metadata.finishTableExecute(session.toConnectorSession(catalogHandle), tableExecuteHandle.getConnectorHandle(), fragments, tableExecuteState);
}
@Override
public void executeTableExecute(Session session, TableExecuteHandle tableExecuteHandle)
{
CatalogHandle catalogHandle = tableExecuteHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
metadata.executeTableExecute(session.toConnectorSession(catalogHandle), tableExecuteHandle.getConnectorHandle());
}
@Override
public Optional getSystemTable(Session session, QualifiedObjectName tableName)
{
requireNonNull(session, "session is null");
requireNonNull(tableName, "tableName is null");
Optional catalog = getOptionalCatalogMetadata(session, tableName.getCatalogName());
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
// we query only main connector for runtime system tables
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
return metadata.getSystemTable(session.toConnectorSession(catalogHandle), tableName.asSchemaTableName());
}
return Optional.empty();
}
@Override
public TableProperties getTableProperties(Session session, TableHandle handle)
{
CatalogHandle catalogHandle = handle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return new TableProperties(catalogHandle, handle.getTransaction(), metadata.getTableProperties(connectorSession, handle.getConnectorHandle()));
}
@Override
public TableHandle makeCompatiblePartitioning(Session session, TableHandle tableHandle, PartitioningHandle partitioningHandle)
{
checkArgument(partitioningHandle.getCatalogHandle().isPresent(), "Expect partitioning handle from connector, got system partitioning handle");
CatalogHandle catalogHandle = partitioningHandle.getCatalogHandle().get();
checkArgument(catalogHandle.equals(tableHandle.getCatalogHandle()), "ConnectorId of tableHandle and partitioningHandle does not match");
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorTransactionHandle transaction = catalogMetadata.getTransactionHandleFor(catalogHandle);
ConnectorTableHandle newTableHandle = metadata.makeCompatiblePartitioning(
session.toConnectorSession(catalogHandle),
tableHandle.getConnectorHandle(),
partitioningHandle.getConnectorHandle());
return new TableHandle(catalogHandle, newTableHandle, transaction);
}
@Override
public Optional getCommonPartitioning(Session session, PartitioningHandle left, PartitioningHandle right)
{
Optional leftCatalogHandle = left.getCatalogHandle();
Optional rightCatalogHandle = right.getCatalogHandle();
if (leftCatalogHandle.isEmpty() || rightCatalogHandle.isEmpty() || !leftCatalogHandle.equals(rightCatalogHandle)) {
return Optional.empty();
}
if (!left.getTransactionHandle().equals(right.getTransactionHandle())) {
return Optional.empty();
}
CatalogHandle catalogHandle = leftCatalogHandle.get();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
Optional commonHandle = metadata.getCommonPartitioningHandle(session.toConnectorSession(catalogHandle), left.getConnectorHandle(), right.getConnectorHandle());
return commonHandle.map(handle -> new PartitioningHandle(Optional.of(catalogHandle), left.getTransactionHandle(), handle));
}
@Override
public Optional getInfo(Session session, TableHandle handle)
{
CatalogHandle catalogHandle = handle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
return metadata.getInfo(handle.getConnectorHandle());
}
@Override
public CatalogSchemaTableName getTableName(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
SchemaTableName tableName = metadata.getTableName(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
return new CatalogSchemaTableName(catalogMetadata.getCatalogName(), tableName);
}
@Override
public TableSchema getTableSchema(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorTableSchema tableSchema = metadata.getTableSchema(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
return new TableSchema(catalogMetadata.getCatalogName(), tableSchema);
}
@Override
public TableMetadata getTableMetadata(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorTableMetadata tableMetadata = metadata.getTableMetadata(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
return new TableMetadata(catalogMetadata.getCatalogName(), tableMetadata);
}
@Override
public TableStatistics getTableStatistics(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
TableStatistics tableStatistics = metadata.getTableStatistics(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
verifyNotNull(tableStatistics, "%s returned null tableStatistics for %s", metadata, tableHandle);
return tableStatistics;
}
@Override
public Map getColumnHandles(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
Map handles = metadata.getColumnHandles(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
ImmutableMap.Builder map = ImmutableMap.builder();
for (Entry mapEntry : handles.entrySet()) {
map.put(mapEntry.getKey().toLowerCase(ENGLISH), mapEntry.getValue());
}
return map.buildOrThrow();
}
@Override
public ColumnMetadata getColumnMetadata(Session session, TableHandle tableHandle, ColumnHandle columnHandle)
{
requireNonNull(tableHandle, "tableHandle is null");
requireNonNull(columnHandle, "columnHandle is null");
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
return metadata.getColumnMetadata(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), columnHandle);
}
@Override
public List listTables(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional objectName = prefix.asQualifiedObjectName();
if (objectName.isPresent()) {
Optional exists = isExistingRelationForListing(session, objectName.get());
if (exists.isPresent()) {
return exists.get() ? ImmutableList.of(objectName.get()) : ImmutableList.of();
}
}
Optional catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Set tables = new LinkedHashSet<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
if (isExternalInformationSchema(catalogHandle, prefix.getSchemaName())) {
continue;
}
metadata.listTables(connectorSession, prefix.getSchemaName()).stream()
.map(convertFromSchemaTableName(prefix.getCatalogName()))
.filter(table -> !isExternalInformationSchema(catalogHandle, table.getSchemaName()))
.forEach(tables::add);
}
}
return ImmutableList.copyOf(tables);
}
private Optional isExistingRelationForListing(Session session, QualifiedObjectName name)
{
if (isMaterializedView(session, name)) {
return Optional.of(true);
}
if (isView(session, name)) {
return Optional.of(true);
}
// TODO: consider a better way to resolve relation names: https://github.com/trinodb/trino/issues/9400
try {
return Optional.of(getRedirectionAwareTableHandle(session, name).tableHandle().isPresent());
}
catch (TrinoException e) {
// ignore redirection errors for consistency with listing
if (e.getErrorCode().equals(TABLE_REDIRECTION_ERROR.toErrorCode())) {
return Optional.of(true);
}
// we don't know if it exists or not
return Optional.empty();
}
}
@Override
public List listTableColumns(Session session, QualifiedTablePrefix prefix, UnaryOperator> relationFilter)
{
requireNonNull(prefix, "prefix is null");
String catalogName = prefix.getCatalogName();
Optional schemaName = prefix.getSchemaName();
Optional relationName = prefix.getTableName();
if (catalogName.isEmpty() ||
(schemaName.isPresent() && schemaName.get().isEmpty()) ||
(relationName.isPresent() && relationName.get().isEmpty())) {
// Cannot exist
return ImmutableList.of();
}
if (relationName.isPresent()) {
QualifiedObjectName objectName = new QualifiedObjectName(catalogName, schemaName.orElseThrow(), relationName.get());
SchemaTableName schemaTableName = objectName.asSchemaTableName();
return Optional.empty()
.or(() -> getMaterializedViewInternal(session, objectName)
.map(materializedView -> RelationColumnsMetadata.forMaterializedView(schemaTableName, materializedView.getColumns())))
.or(() -> getViewInternal(session, objectName)
.map(view -> RelationColumnsMetadata.forView(schemaTableName, view.getColumns())))
.or(() -> {
try {
// TODO: redirects are handled inefficiently: we currently throw-away redirect info and redo it later
RedirectionAwareTableHandle redirectionAware = getRedirectionAwareTableHandle(session, objectName);
if (redirectionAware.redirectedTableName().isPresent()) {
return Optional.of(RelationColumnsMetadata.forRedirectedTable(schemaTableName));
}
if (redirectionAware.tableHandle().isPresent()) {
return Optional.of(RelationColumnsMetadata.forTable(schemaTableName, getTableMetadata(session, redirectionAware.tableHandle().get()).getColumns()));
}
}
catch (RuntimeException e) {
boolean silent = false;
if (e instanceof TrinoException trinoException) {
ErrorCode errorCode = trinoException.getErrorCode();
silent = errorCode.equals(UNSUPPORTED_TABLE_TYPE.toErrorCode()) ||
// e.g. table deleted concurrently
errorCode.equals(NOT_FOUND.toErrorCode()) ||
// e.g. Iceberg/Delta table being deleted concurrently resulting in failure to load metadata from filesystem
errorCode.getType() == EXTERNAL;
}
if (silent) {
log.debug(e, "Failed to get metadata for table: %s", objectName);
}
else {
log.warn(e, "Failed to get metadata for table: %s", objectName);
}
}
// Not found, or getting metadata failed.
return Optional.empty();
})
.filter(relationColumnsMetadata -> relationFilter.apply(ImmutableSet.of(relationColumnsMetadata.name())).contains(relationColumnsMetadata.name()))
.map(relationColumnsMetadata -> ImmutableList.of(tableColumnsMetadata(catalogName, relationColumnsMetadata)))
.orElse(ImmutableList.of());
}
Optional catalog = getOptionalCatalogMetadata(session, catalogName);
Map tableColumns = new HashMap<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
if (isExternalInformationSchema(catalogHandle, schemaName)) {
continue;
}
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
metadata.streamRelationColumns(connectorSession, schemaName, relationFilter)
.forEachRemaining(relationColumnsMetadata -> {
if (!isExternalInformationSchema(catalogHandle, relationColumnsMetadata.name().getSchemaName())) {
// putIfAbsent to resolve any potential conflicts between system tables and regular tables
tableColumns.putIfAbsent(relationColumnsMetadata.name(), tableColumnsMetadata(catalogName, relationColumnsMetadata));
}
});
}
}
return ImmutableList.copyOf(tableColumns.values());
}
private TableColumnsMetadata tableColumnsMetadata(String catalogName, RelationColumnsMetadata relationColumnsMetadata)
{
SchemaTableName relationName = relationColumnsMetadata.name();
Optional> columnsMetadata = Optional.>empty()
.or(() -> relationColumnsMetadata.materializedViewColumns()
.map(columns -> materializedViewColumnMetadata(catalogName, relationName, columns)))
.or(() -> relationColumnsMetadata.viewColumns()
.map(columns -> viewColumnMetadata(catalogName, relationName, columns)))
.or(relationColumnsMetadata::tableColumns)
.or(() -> {
checkState(relationColumnsMetadata.redirected(), "Invalid RelationColumnsMetadata: %s", relationColumnsMetadata);
return Optional.empty();
});
return new TableColumnsMetadata(relationName, columnsMetadata);
}
private List materializedViewColumnMetadata(String catalogName, SchemaTableName materializedViewName, List columns)
{
ImmutableList.Builder columnMetadata = ImmutableList.builderWithExpectedSize(columns.size());
for (ConnectorMaterializedViewDefinition.Column column : columns) {
try {
columnMetadata.add(ColumnMetadata.builder()
.setName(column.getName())
.setType(typeManager.getType(column.getType()))
.setComment(column.getComment())
.build());
}
catch (TypeNotFoundException e) {
QualifiedObjectName name = new QualifiedObjectName(catalogName, materializedViewName.getSchemaName(), materializedViewName.getTableName());
throw new TrinoException(INVALID_VIEW, format("Unknown type '%s' for column '%s' in materialized view: %s", column.getType(), column.getName(), name));
}
}
return columnMetadata.build();
}
private List viewColumnMetadata(String catalogName, SchemaTableName viewName, List columns)
{
ImmutableList.Builder columnMetadata = ImmutableList.builderWithExpectedSize(columns.size());
for (ConnectorViewDefinition.ViewColumn column : columns) {
try {
columnMetadata.add(ColumnMetadata.builder()
.setName(column.getName())
.setType(typeManager.getType(column.getType()))
.setComment(column.getComment())
.build());
}
catch (TypeNotFoundException e) {
QualifiedObjectName name = new QualifiedObjectName(catalogName, viewName.getSchemaName(), viewName.getTableName());
throw new TrinoException(INVALID_VIEW, format("Unknown type '%s' for column '%s' in view: %s", column.getType(), column.getName(), name));
}
}
return columnMetadata.build();
}
@Override
public List listRelationComments(Session session, String catalogName, Optional schemaName, UnaryOperator> relationFilter)
{
Optional catalog = getOptionalCatalogMetadata(session, catalogName);
ImmutableList.Builder tableComments = ImmutableList.builder();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
if (isExternalInformationSchema(catalogHandle, schemaName)) {
continue;
}
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
stream(metadata.streamRelationComments(connectorSession, schemaName, relationFilter))
.filter(commentMetadata -> !isExternalInformationSchema(catalogHandle, commentMetadata.name().getSchemaName()))
.forEach(tableComments::add);
}
}
return tableComments.build();
}
@Override
public void createSchema(Session session, CatalogSchemaName schema, Map properties, TrinoPrincipal principal)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schema.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.createSchema(session.toConnectorSession(catalogHandle), schema.getSchemaName(), properties, principal);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.schemaCreated(session, schema);
}
}
@Override
public void dropSchema(Session session, CatalogSchemaName schema, boolean cascade)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schema.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.dropSchema(session.toConnectorSession(catalogHandle), schema.getSchemaName(), cascade);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.schemaDropped(session, schema);
}
}
@Override
public void renameSchema(Session session, CatalogSchemaName source, String target)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, source.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.renameSchema(session.toConnectorSession(catalogHandle), source.getSchemaName(), target);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.schemaRenamed(session, source, new CatalogSchemaName(source.getCatalogName(), target));
}
}
@Override
public void setSchemaAuthorization(Session session, CatalogSchemaName source, TrinoPrincipal principal)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, source.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.setSchemaOwner(session, source, principal);
}
else {
metadata.setSchemaAuthorization(session.toConnectorSession(catalogHandle), source.getSchemaName(), principal);
}
}
@Override
public void createTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata, SaveMode saveMode)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.createTable(session.toConnectorSession(catalogHandle), tableMetadata, saveMode);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableCreated(session, new CatalogSchemaTableName(catalogName, tableMetadata.getTable()));
}
}
@Override
public void renameTable(Session session, TableHandle tableHandle, CatalogSchemaTableName sourceTableName, QualifiedObjectName newTableName)
{
String catalogName = newTableName.getCatalogName();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
if (!tableHandle.getCatalogHandle().equals(catalogHandle)) {
throw new TrinoException(SYNTAX_ERROR, "Cannot rename tables across catalogs");
}
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.renameTable(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), newTableName.asSchemaTableName());
if (catalogMetadata.getSecurityManagement() != CONNECTOR) {
systemSecurityMetadata.tableRenamed(session, sourceTableName, newTableName.asCatalogSchemaTableName());
}
}
@Override
public void setTableProperties(Session session, TableHandle tableHandle, Map> properties)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.setTableProperties(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), properties);
}
@Override
public void setTableComment(Session session, TableHandle tableHandle, Optional comment)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.setTableComment(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), comment);
}
@Override
public void setViewComment(Session session, QualifiedObjectName viewName, Optional comment)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.setViewComment(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), comment);
}
@Override
public void setViewColumnComment(Session session, QualifiedObjectName viewName, String columnName, Optional comment)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.setViewColumnComment(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), columnName, comment);
}
@Override
public void setColumnComment(Session session, TableHandle tableHandle, ColumnHandle column, Optional comment)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.setColumnComment(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), column, comment);
}
@Override
public void renameColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnHandle source, String target)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle.getCatalogName());
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.renameColumn(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), source, target.toLowerCase(ENGLISH));
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
ColumnMetadata columnMetadata = getColumnMetadata(session, tableHandle, source);
systemSecurityMetadata.columnRenamed(session, table, columnMetadata.getName(), target);
}
}
@Override
public void renameField(Session session, TableHandle tableHandle, List fieldPath, String target)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.renameField(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), fieldPath, target.toLowerCase(ENGLISH));
}
@Override
public void addColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnMetadata column)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle.getCatalogName());
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.addColumn(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), column);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.columnCreated(session, table, column.getName());
}
}
@Override
public void addField(Session session, TableHandle tableHandle, List parentPath, String fieldName, Type type, boolean ignoreExisting)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.addField(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), parentPath, fieldName, type, ignoreExisting);
}
@Override
public void dropColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnHandle column)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle.getCatalogName());
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.dropColumn(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), column);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
ColumnMetadata columnMetadata = getColumnMetadata(session, tableHandle, column);
systemSecurityMetadata.columnDropped(session, table, columnMetadata.getName());
}
}
@Override
public void dropField(Session session, TableHandle tableHandle, ColumnHandle column, List fieldPath)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.dropField(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), column, fieldPath);
}
@Override
public void setColumnType(Session session, TableHandle tableHandle, ColumnHandle column, Type type)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.setColumnType(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), column, type);
}
@Override
public void setFieldType(Session session, TableHandle tableHandle, List fieldPath, Type type)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.setFieldType(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), fieldPath, type);
}
@Override
public void setTableAuthorization(Session session, CatalogSchemaTableName table, TrinoPrincipal principal)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, table.getCatalogName());
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.setTableOwner(session, table, principal);
}
else {
metadata.setTableAuthorization(session.toConnectorSession(catalogMetadata.getCatalogHandle()), table.getSchemaTableName(), principal);
}
}
@Override
public void dropTable(Session session, TableHandle tableHandle, CatalogSchemaTableName tableName)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.dropTable(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
if (catalogMetadata.getSecurityManagement() != CONNECTOR) {
systemSecurityMetadata.tableDropped(session, tableName);
}
}
@Override
public void truncateTable(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.truncateTable(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
}
@Override
public Optional getInsertLayout(Session session, TableHandle table)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return metadata.getInsertLayout(session.toConnectorSession(catalogHandle), table.getConnectorHandle())
.map(layout -> new TableLayout(catalogHandle, catalogMetadata.getTransactionHandleFor(catalogHandle), layout));
}
@Override
public TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(Session session, CatalogHandle catalogHandle, ConnectorTableMetadata tableMetadata)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return metadata.getStatisticsCollectionMetadataForWrite(session.toConnectorSession(catalogHandle), tableMetadata);
}
@Override
public AnalyzeMetadata getStatisticsCollectionMetadata(Session session, TableHandle tableHandle, Map analyzeProperties)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorAnalyzeMetadata analyze = metadata.getStatisticsCollectionMetadata(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), analyzeProperties);
return new AnalyzeMetadata(analyze.getStatisticsMetadata(), new TableHandle(catalogHandle, analyze.getTableHandle(), tableHandle.getTransaction()));
}
@Override
public AnalyzeTableHandle beginStatisticsCollection(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(catalogHandle);
ConnectorTableHandle connectorTableHandle = metadata.beginStatisticsCollection(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
return new AnalyzeTableHandle(catalogHandle, transactionHandle, connectorTableHandle);
}
@Override
public void finishStatisticsCollection(Session session, AnalyzeTableHandle tableHandle, Collection computedStatistics)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
catalogMetadata.getMetadata(session).finishStatisticsCollection(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), computedStatistics);
}
@Override
public Optional getNewTableLayout(Session session, String catalogName, ConnectorTableMetadata tableMetadata)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.getNewTableLayout(connectorSession, tableMetadata)
.map(layout -> new TableLayout(catalogHandle, transactionHandle, layout));
}
@Override
public Optional getSupportedType(Session session, CatalogHandle catalogHandle, Map tableProperties, Type type)
{
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return metadata.getSupportedType(session.toConnectorSession(catalogHandle), tableProperties, type)
.map(newType -> {
if (!typeCoercion.isCompatible(newType, type)) {
throw new TrinoException(FUNCTION_IMPLEMENTATION_ERROR, format("Type '%s' is not compatible with the supplied type '%s' in getSupportedType", type, newType));
}
return newType;
});
}
@Override
public void beginQuery(Session session)
{
languageFunctionManager.registerQuery(session);
}
@Override
public void cleanupQuery(Session session)
{
QueryCatalogs queryCatalogs = catalogsByQueryId.remove(session.getQueryId());
if (queryCatalogs != null) {
queryCatalogs.finish();
}
languageFunctionManager.unregisterQuery(session);
}
@Override
public OutputTableHandle beginCreateTable(Session session, String catalogName, ConnectorTableMetadata tableMetadata, Optional layout, boolean replace)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName);
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ConnectorOutputTableHandle handle = metadata.beginCreateTable(connectorSession, tableMetadata, layout.map(TableLayout::getLayout), getRetryPolicy(session).getRetryMode(), replace);
return new OutputTableHandle(catalogHandle, tableMetadata.getTable(), transactionHandle, handle);
}
@Override
public Optional finishCreateTable(Session session, OutputTableHandle tableHandle, Collection fragments, Collection computedStatistics)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
Optional output = metadata.finishCreateTable(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), fragments, computedStatistics);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableCreated(session, new CatalogSchemaTableName(catalogHandle.getCatalogName(), tableHandle.getTableName()));
}
return output;
}
@Override
public InsertTableHandle beginInsert(Session session, TableHandle tableHandle, List columns)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(catalogHandle);
ConnectorInsertTableHandle handle = metadata.beginInsert(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), columns, getRetryPolicy(session).getRetryMode());
return new InsertTableHandle(tableHandle.getCatalogHandle(), transactionHandle, handle);
}
@Override
public boolean supportsMissingColumnsOnInsert(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
return catalogMetadata.getMetadata(session).supportsMissingColumnsOnInsert();
}
@Override
public Optional finishInsert(Session session, InsertTableHandle tableHandle, Collection fragments, Collection computedStatistics)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
return metadata.finishInsert(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), fragments, computedStatistics);
}
@Override
public boolean delegateMaterializedViewRefreshToConnector(Session session, QualifiedObjectName viewName)
{
CatalogMetadata catalogMetadata = getRequiredCatalogMetadata(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return metadata.delegateMaterializedViewRefreshToConnector(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName());
}
@Override
public ListenableFuture refreshMaterializedView(Session session, QualifiedObjectName viewName)
{
CatalogMetadata catalogMetadata = getRequiredCatalogMetadata(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return asVoid(toListenableFuture(metadata.refreshMaterializedView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName())));
}
private static ListenableFuture asVoid(ListenableFuture future)
{
return Futures.transform(future, v -> null, directExecutor());
}
@Override
public InsertTableHandle beginRefreshMaterializedView(Session session, TableHandle tableHandle, List sourceTableHandles)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(catalogHandle);
List sourceConnectorHandles = sourceTableHandles.stream()
.map(TableHandle::getConnectorHandle)
.collect(Collectors.toList());
sourceConnectorHandles.add(tableHandle.getConnectorHandle());
ConnectorInsertTableHandle handle = metadata.beginRefreshMaterializedView(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), sourceConnectorHandles, getRetryPolicy(session).getRetryMode());
return new InsertTableHandle(tableHandle.getCatalogHandle(), transactionHandle, handle);
}
@Override
public Optional finishRefreshMaterializedView(
Session session,
TableHandle tableHandle,
InsertTableHandle insertHandle,
Collection fragments,
Collection computedStatistics,
List sourceTableHandles)
{
CatalogHandle catalogHandle = insertHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
List sourceConnectorHandles = sourceTableHandles.stream()
.map(TableHandle::getConnectorHandle)
.collect(toImmutableList());
return metadata.finishRefreshMaterializedView(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), insertHandle.getConnectorHandle(),
fragments, computedStatistics, sourceConnectorHandles);
}
@Override
public ColumnHandle getMergeRowIdColumnHandle(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
return metadata.getMergeRowIdColumnHandle(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
}
@Override
public Optional getUpdateLayout(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(catalogHandle);
return metadata.getUpdateLayout(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle())
.map(partitioning -> new PartitioningHandle(Optional.of(catalogHandle), Optional.of(transactionHandle), partitioning));
}
@Override
public Optional applyUpdate(Session session, TableHandle table, Map assignments)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyUpdate(connectorSession, table.getConnectorHandle(), assignments)
.map(newHandle -> new TableHandle(catalogHandle, newHandle, table.getTransaction()));
}
@Override
public OptionalLong executeUpdate(Session session, TableHandle table)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.executeUpdate(connectorSession, table.getConnectorHandle());
}
@Override
public Optional applyDelete(Session session, TableHandle table)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyDelete(connectorSession, table.getConnectorHandle())
.map(newHandle -> new TableHandle(catalogHandle, newHandle, table.getTransaction()));
}
@Override
public OptionalLong executeDelete(Session session, TableHandle table)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.executeDelete(connectorSession, table.getConnectorHandle());
}
@Override
public RowChangeParadigm getRowChangeParadigm(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
return metadata.getRowChangeParadigm(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle());
}
@Override
public MergeHandle beginMerge(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
ConnectorMergeTableHandle newHandle = metadata.beginMerge(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), getRetryPolicy(session).getRetryMode());
return new MergeHandle(tableHandle.withConnectorHandle(newHandle.getTableHandle()), newHandle);
}
@Override
public void finishMerge(Session session, MergeHandle mergeHandle, Collection fragments, Collection computedStatistics)
{
CatalogHandle catalogHandle = mergeHandle.getTableHandle().getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
metadata.finishMerge(session.toConnectorSession(catalogHandle), mergeHandle.getConnectorMergeHandle(), fragments, computedStatistics);
}
@Override
public Optional getCatalogHandle(Session session, String catalogName)
{
return transactionManager.getOptionalCatalogMetadata(session.getRequiredTransactionId(), catalogName).map(CatalogMetadata::getCatalogHandle);
}
@Override
public List listCatalogs(Session session)
{
return transactionManager.getCatalogs(session.getRequiredTransactionId());
}
@Override
public List listViews(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional objectName = prefix.asQualifiedObjectName();
if (objectName.isPresent()) {
return getView(session, objectName.get())
.map(handle -> ImmutableList.of(objectName.get()))
.orElseGet(ImmutableList::of);
}
Optional catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Set views = new LinkedHashSet<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
if (isExternalInformationSchema(catalogHandle, prefix.getSchemaName())) {
continue;
}
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
metadata.listViews(connectorSession, prefix.getSchemaName()).stream()
.map(convertFromSchemaTableName(prefix.getCatalogName()))
.filter(view -> !isExternalInformationSchema(catalogHandle, view.getSchemaName()))
.forEach(views::add);
}
}
return ImmutableList.copyOf(views);
}
@Override
public Map getViews(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Map views = new LinkedHashMap<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
SchemaTablePrefix tablePrefix = prefix.asSchemaTablePrefix();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
if (isExternalInformationSchema(catalogHandle, tablePrefix.getSchema())) {
continue;
}
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
Map viewMap;
if (tablePrefix.getTable().isPresent()) {
viewMap = metadata.getView(connectorSession, tablePrefix.toSchemaTableName())
.map(view -> ImmutableMap.of(tablePrefix.toSchemaTableName(), view))
.orElse(ImmutableMap.of());
}
else {
viewMap = metadata.getViews(connectorSession, tablePrefix.getSchema());
}
for (Entry entry : viewMap.entrySet()) {
if (isExternalInformationSchema(catalogHandle, entry.getKey().getSchemaName())) {
continue;
}
QualifiedObjectName viewName = new QualifiedObjectName(
prefix.getCatalogName(),
entry.getKey().getSchemaName(),
entry.getKey().getTableName());
views.put(viewName, new ViewInfo(entry.getValue()));
}
}
}
return ImmutableMap.copyOf(views);
}
@Override
public Map getSchemaProperties(Session session, CatalogSchemaName schemaName)
{
if (!schemaExists(session, schemaName)) {
throw new TrinoException(SCHEMA_NOT_FOUND, format("Schema '%s' does not exist", schemaName));
}
CatalogMetadata catalogMetadata = getRequiredCatalogMetadata(session, schemaName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getConnectorHandleForSchema(schemaName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.getSchemaProperties(connectorSession, schemaName.getSchemaName());
}
@Override
public Optional getSchemaOwner(Session session, CatalogSchemaName schemaName)
{
if (!schemaExists(session, schemaName)) {
throw new TrinoException(SCHEMA_NOT_FOUND, format("Schema '%s' does not exist", schemaName));
}
CatalogMetadata catalogMetadata = getRequiredCatalogMetadata(session, schemaName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
return systemSecurityMetadata.getSchemaOwner(session, schemaName);
}
CatalogHandle catalogHandle = catalogMetadata.getConnectorHandleForSchema(schemaName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.getSchemaOwner(connectorSession, schemaName.getSchemaName());
}
@Override
public boolean isView(Session session, QualifiedObjectName viewName)
{
return getViewInternal(session, viewName).isPresent();
}
@Override
public Optional getView(Session session, QualifiedObjectName viewName)
{
Optional connectorView = getViewInternal(session, viewName);
if (connectorView.isEmpty() || connectorView.get().isRunAsInvoker() || isCatalogManagedSecurity(session, viewName.getCatalogName())) {
return connectorView.map(view -> createViewDefinition(viewName, view, view.getOwner().map(Identity::ofUser)));
}
Identity runAsIdentity = systemSecurityMetadata.getViewRunAsIdentity(session, viewName.asCatalogSchemaTableName())
.or(() -> connectorView.get().getOwner().map(Identity::ofUser))
.orElseThrow(() -> new TrinoException(NOT_SUPPORTED, "Catalog does not support run-as DEFINER views: " + viewName));
return Optional.of(createViewDefinition(viewName, connectorView.get(), Optional.of(runAsIdentity)));
}
private static ViewDefinition createViewDefinition(QualifiedObjectName viewName, ConnectorViewDefinition view, Optional runAsIdentity)
{
if (view.isRunAsInvoker() && runAsIdentity.isPresent()) {
throw new TrinoException(INVALID_VIEW, "Run-as identity cannot be set for a run-as invoker view: " + viewName);
}
if (!view.isRunAsInvoker() && runAsIdentity.isEmpty()) {
throw new TrinoException(INVALID_VIEW, "Run-as identity must be set for a run-as definer view: " + viewName);
}
return new ViewDefinition(
view.getOriginalSql(),
view.getCatalog(),
view.getSchema(),
view.getColumns().stream()
.map(column -> new ViewColumn(column.getName(), column.getType(), column.getComment()))
.collect(toImmutableList()),
view.getComment(),
runAsIdentity,
view.getPath());
}
private Optional getViewInternal(Session session, QualifiedObjectName viewName)
{
if (viewName.getCatalogName().isEmpty() || viewName.getSchemaName().isEmpty() || viewName.getObjectName().isEmpty()) {
// View cannot exist
return Optional.empty();
}
Optional catalog = getOptionalCatalogMetadata(session, viewName.getCatalogName());
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(session, viewName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.getView(connectorSession, viewName.asSchemaTableName());
}
return Optional.empty();
}
@Override
public void createView(Session session, QualifiedObjectName viewName, ViewDefinition definition, boolean replace)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.createView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), definition.toConnectorViewDefinition(), replace);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableCreated(session, viewName.asCatalogSchemaTableName());
}
}
@Override
public void renameView(Session session, QualifiedObjectName source, QualifiedObjectName target)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, target.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
if (!source.getCatalogName().equals(target.getCatalogName())) {
throw new TrinoException(SYNTAX_ERROR, "Cannot rename views across catalogs");
}
metadata.renameView(session.toConnectorSession(catalogHandle), source.asSchemaTableName(), target.asSchemaTableName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableRenamed(session, source.asCatalogSchemaTableName(), target.asCatalogSchemaTableName());
}
}
@Override
public void setViewAuthorization(Session session, CatalogSchemaTableName view, TrinoPrincipal principal)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, view.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.setViewOwner(session, view, principal);
}
else {
metadata.setViewAuthorization(session.toConnectorSession(catalogHandle), view.getSchemaTableName(), principal);
}
}
@Override
public void dropView(Session session, QualifiedObjectName viewName)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.dropView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableDropped(session, viewName.asCatalogSchemaTableName());
}
}
@Override
public void createMaterializedView(Session session, QualifiedObjectName viewName, MaterializedViewDefinition definition, boolean replace, boolean ignoreExisting)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.createMaterializedView(
session.toConnectorSession(catalogHandle),
viewName.asSchemaTableName(),
definition.toConnectorMaterializedViewDefinition(),
replace,
ignoreExisting);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableCreated(session, viewName.asCatalogSchemaTableName());
}
}
@Override
public void dropMaterializedView(Session session, QualifiedObjectName viewName)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.dropMaterializedView(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableDropped(session, viewName.asCatalogSchemaTableName());
}
}
@Override
public List listMaterializedViews(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional objectName = prefix.asQualifiedObjectName();
if (objectName.isPresent()) {
return isMaterializedView(session, objectName.get()) ? ImmutableList.of(objectName.get()) : ImmutableList.of();
}
Optional catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Set materializedViews = new LinkedHashSet<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
if (isExternalInformationSchema(catalogHandle, prefix.getSchemaName())) {
continue;
}
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
metadata.listMaterializedViews(connectorSession, prefix.getSchemaName()).stream()
.map(convertFromSchemaTableName(prefix.getCatalogName()))
.filter(materializedView -> !isExternalInformationSchema(catalogHandle, materializedView.getSchemaName()))
.forEach(materializedViews::add);
}
}
return ImmutableList.copyOf(materializedViews);
}
@Override
public Map getMaterializedViews(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
Map views = new LinkedHashMap<>();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
SchemaTablePrefix tablePrefix = prefix.asSchemaTablePrefix();
for (CatalogHandle catalogHandle : catalogMetadata.listCatalogHandles()) {
if (isExternalInformationSchema(catalogHandle, tablePrefix.getSchema())) {
continue;
}
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
Map materializedViewMap;
if (tablePrefix.getTable().isPresent()) {
materializedViewMap = metadata.getMaterializedView(connectorSession, tablePrefix.toSchemaTableName())
.map(view -> ImmutableMap.of(tablePrefix.toSchemaTableName(), view))
.orElse(ImmutableMap.of());
}
else {
materializedViewMap = metadata.getMaterializedViews(connectorSession, tablePrefix.getSchema());
}
for (Entry entry : materializedViewMap.entrySet()) {
if (isExternalInformationSchema(catalogHandle, entry.getKey().getSchemaName())) {
continue;
}
QualifiedObjectName viewName = new QualifiedObjectName(
prefix.getCatalogName(),
entry.getKey().getSchemaName(),
entry.getKey().getTableName());
views.put(viewName, new ViewInfo(entry.getValue()));
}
}
}
return ImmutableMap.copyOf(views);
}
@Override
public boolean isMaterializedView(Session session, QualifiedObjectName viewName)
{
return getMaterializedViewInternal(session, viewName).isPresent();
}
@Override
public Optional getMaterializedView(Session session, QualifiedObjectName viewName)
{
Optional connectorView = getMaterializedViewInternal(session, viewName);
if (connectorView.isEmpty() || isCatalogManagedSecurity(session, viewName.getCatalogName())) {
return connectorView.map(view -> {
String runAsUser = view.getOwner().orElseThrow(() -> new TrinoException(INVALID_VIEW, "Owner not set for a run-as invoker view: " + viewName));
return createMaterializedViewDefinition(view, Identity.ofUser(runAsUser));
});
}
Identity runAsIdentity = systemSecurityMetadata.getViewRunAsIdentity(session, viewName.asCatalogSchemaTableName())
.or(() -> connectorView.get().getOwner().map(Identity::ofUser))
.orElseThrow(() -> new TrinoException(NOT_SUPPORTED, "Materialized view does not have an owner: " + viewName));
return Optional.of(createMaterializedViewDefinition(connectorView.get(), runAsIdentity));
}
private static MaterializedViewDefinition createMaterializedViewDefinition(ConnectorMaterializedViewDefinition view, Identity runAsIdentity)
{
return new MaterializedViewDefinition(
view.getOriginalSql(),
view.getCatalog(),
view.getSchema(),
view.getColumns().stream()
.map(column -> new ViewColumn(column.getName(), column.getType(), Optional.empty()))
.collect(toImmutableList()),
view.getGracePeriod(),
view.getComment(),
runAsIdentity,
view.getPath(),
view.getStorageTable(),
view.getProperties());
}
private Optional getMaterializedViewInternal(Session session, QualifiedObjectName viewName)
{
if (viewName.getCatalogName().isEmpty() || viewName.getSchemaName().isEmpty() || viewName.getObjectName().isEmpty()) {
// View cannot exist
return Optional.empty();
}
Optional catalog = getOptionalCatalogMetadata(session, viewName.getCatalogName());
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(session, viewName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.getMaterializedView(connectorSession, viewName.asSchemaTableName());
}
return Optional.empty();
}
@Override
public MaterializedViewFreshness getMaterializedViewFreshness(Session session, QualifiedObjectName viewName)
{
Optional catalog = getOptionalCatalogMetadata(session, viewName.getCatalogName());
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(session, viewName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.getMaterializedViewFreshness(connectorSession, viewName.asSchemaTableName());
}
return new MaterializedViewFreshness(STALE);
}
@Override
public void renameMaterializedView(Session session, QualifiedObjectName source, QualifiedObjectName target)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, target.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
if (!source.getCatalogName().equals(target.getCatalogName())) {
throw new TrinoException(SYNTAX_ERROR, "Cannot rename materialized views across catalogs");
}
metadata.renameMaterializedView(session.toConnectorSession(catalogHandle), source.asSchemaTableName(), target.asSchemaTableName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.tableRenamed(session, source.asCatalogSchemaTableName(), target.asCatalogSchemaTableName());
}
}
@Override
public void setMaterializedViewProperties(Session session, QualifiedObjectName viewName, Map> properties)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.setMaterializedViewProperties(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), properties);
}
@Override
public void setMaterializedViewColumnComment(Session session, QualifiedObjectName viewName, String columnName, Optional comment)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, viewName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.setMaterializedViewColumnComment(session.toConnectorSession(catalogHandle), viewName.asSchemaTableName(), columnName, comment);
}
private static boolean isExternalInformationSchema(CatalogHandle catalogHandle, Optional schemaName)
{
return schemaName.isPresent() && isExternalInformationSchema(catalogHandle, schemaName.get());
}
private static boolean isExternalInformationSchema(CatalogHandle catalogHandle, String schemaName)
{
return !catalogHandle.getType().isInternal() && "information_schema".equalsIgnoreCase(schemaName);
}
@Override
public Optional applyTableScanRedirect(Session session, TableHandle tableHandle)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyTableScanRedirect(connectorSession, tableHandle.getConnectorHandle());
}
private QualifiedObjectName getRedirectedTableName(Session session, QualifiedObjectName originalTableName)
{
requireNonNull(session, "session is null");
requireNonNull(originalTableName, "originalTableName is null");
if (originalTableName.getCatalogName().isEmpty() || originalTableName.getSchemaName().isEmpty() || originalTableName.getObjectName().isEmpty()) {
// table cannot exist
return originalTableName;
}
QualifiedObjectName tableName = originalTableName;
Set visitedTableNames = new LinkedHashSet<>();
visitedTableNames.add(tableName);
for (int count = 0; count < MAX_TABLE_REDIRECTIONS; count++) {
Optional catalog = getOptionalCatalogMetadata(session, tableName.getCatalogName());
if (catalog.isEmpty()) {
// Stop redirection
return tableName;
}
CatalogMetadata catalogMetadata = catalog.get();
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(session, tableName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
Optional redirectedTableName = metadata.redirectTable(session.toConnectorSession(catalogHandle), tableName.asSchemaTableName())
.map(name -> convertFromSchemaTableName(name.getCatalogName()).apply(name.getSchemaTableName()));
if (redirectedTableName.isEmpty()) {
return tableName;
}
tableName = redirectedTableName.get();
// Check for loop in redirection
if (!visitedTableNames.add(tableName)) {
throw new TrinoException(TABLE_REDIRECTION_ERROR,
format("Table redirections form a loop: %s",
Streams.concat(visitedTableNames.stream(), Stream.of(tableName))
.map(QualifiedObjectName::toString)
.collect(Collectors.joining(" -> "))));
}
}
throw new TrinoException(TABLE_REDIRECTION_ERROR, format("Table redirected too many times (%d): %s", MAX_TABLE_REDIRECTIONS, visitedTableNames));
}
@Override
public RedirectionAwareTableHandle getRedirectionAwareTableHandle(Session session, QualifiedObjectName tableName)
{
return getRedirectionAwareTableHandle(session, tableName, Optional.empty(), Optional.empty());
}
@Override
public RedirectionAwareTableHandle getRedirectionAwareTableHandle(Session session, QualifiedObjectName tableName, Optional startVersion, Optional endVersion)
{
QualifiedObjectName targetTableName = getRedirectedTableName(session, tableName);
if (targetTableName.equals(tableName)) {
return noRedirection(getTableHandle(session, tableName, startVersion, endVersion));
}
Optional tableHandle = getTableHandle(session, targetTableName, startVersion, endVersion);
if (tableHandle.isPresent()) {
return withRedirectionTo(targetTableName, tableHandle.get());
}
// Redirected table must exist
if (getCatalogHandle(session, targetTableName.getCatalogName()).isEmpty()) {
throw new TrinoException(TABLE_REDIRECTION_ERROR, format("Table '%s' redirected to '%s', but the target catalog '%s' does not exist", tableName, targetTableName, targetTableName.getCatalogName()));
}
if (!schemaExists(session, new CatalogSchemaName(targetTableName.getCatalogName(), targetTableName.getSchemaName()))) {
throw new TrinoException(TABLE_REDIRECTION_ERROR, format("Table '%s' redirected to '%s', but the target schema '%s' does not exist", tableName, targetTableName, targetTableName.getSchemaName()));
}
throw new TrinoException(TABLE_REDIRECTION_ERROR, format("Table '%s' redirected to '%s', but the target table '%s' does not exist", tableName, targetTableName, targetTableName));
}
@Override
public Optional resolveIndex(Session session, TableHandle tableHandle, Set indexableColumns, Set outputColumns, TupleDomain tupleDomain)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
CatalogMetadata catalogMetadata = getCatalogMetadata(session, catalogHandle);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
ConnectorTransactionHandle transaction = catalogMetadata.getTransactionHandleFor(catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
Optional resolvedIndex = metadata.resolveIndex(connectorSession, tableHandle.getConnectorHandle(), indexableColumns, outputColumns, tupleDomain);
return resolvedIndex.map(resolved -> new ResolvedIndex(tableHandle.getCatalogHandle(), transaction, resolved));
}
@Override
public Optional> applyLimit(Session session, TableHandle table, long limit)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyLimit(connectorSession, table.getConnectorHandle(), limit)
.map(result -> new LimitApplicationResult<>(
new TableHandle(catalogHandle, result.getHandle(), table.getTransaction()),
result.isLimitGuaranteed(),
result.isPrecalculateStatistics()));
}
@Override
public Optional> applySample(Session session, TableHandle table, SampleType sampleType, double sampleRatio)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applySample(connectorSession, table.getConnectorHandle(), sampleType, sampleRatio)
.map(result -> new SampleApplicationResult<>(new TableHandle(
catalogHandle,
result.getHandle(),
table.getTransaction()),
result.isPrecalculateStatistics()));
}
@Override
public Optional> applyAggregation(
Session session,
TableHandle table,
List aggregations,
Map assignments,
List> groupingSets)
{
// Global aggregation is represented by [[]]
checkArgument(!groupingSets.isEmpty(), "No grouping sets provided");
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyAggregation(connectorSession, table.getConnectorHandle(), aggregations, assignments, groupingSets)
.map(result -> {
verifyProjection(table, result.getProjections(), result.getAssignments(), aggregations.size());
return new AggregationApplicationResult<>(
new TableHandle(catalogHandle, result.getHandle(), table.getTransaction()),
result.getProjections(),
result.getAssignments(),
result.getGroupingColumnMapping(),
result.isPrecalculateStatistics());
});
}
@Override
public Optional> applyJoin(
Session session,
JoinType joinType,
TableHandle left,
TableHandle right,
ConnectorExpression joinCondition,
Map leftAssignments,
Map rightAssignments,
JoinStatistics statistics)
{
if (!right.getCatalogHandle().equals(left.getCatalogHandle())) {
// Exact comparison is fine as catalog name here is passed from CatalogMetadata and is normalized to lowercase
return Optional.empty();
}
CatalogHandle catalogHandle = left.getCatalogHandle();
ConnectorTransactionHandle transaction = left.getTransaction();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
Optional> connectorResult =
metadata.applyJoin(
connectorSession,
joinType,
left.getConnectorHandle(),
right.getConnectorHandle(),
joinCondition,
leftAssignments,
rightAssignments,
statistics);
return connectorResult.map(result -> {
Set leftColumnHandles = ImmutableSet.copyOf(getColumnHandles(session, left).values());
Set rightColumnHandles = ImmutableSet.copyOf(getColumnHandles(session, right).values());
Set leftColumnHandlesMappingKeys = result.getLeftColumnHandles().keySet();
Set rightColumnHandlesMappingKeys = result.getRightColumnHandles().keySet();
if (leftColumnHandlesMappingKeys.size() != leftColumnHandles.size()
|| rightColumnHandlesMappingKeys.size() != rightColumnHandles.size()
|| !leftColumnHandlesMappingKeys.containsAll(leftColumnHandles)
|| !rightColumnHandlesMappingKeys.containsAll(rightColumnHandles)) {
throw new IllegalStateException(format(
"Column handle mappings do not match old column handles: left=%s; right=%s; newLeft=%s, newRight=%s",
leftColumnHandles,
rightColumnHandles,
leftColumnHandlesMappingKeys,
rightColumnHandlesMappingKeys));
}
return new JoinApplicationResult<>(
new TableHandle(
catalogHandle,
result.getTableHandle(),
transaction),
result.getLeftColumnHandles(),
result.getRightColumnHandles(),
result.isPrecalculateStatistics());
});
}
@Override
public Optional> applyTopN(
Session session,
TableHandle table,
long topNCount,
List sortItems,
Map assignments)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyTopN(connectorSession, table.getConnectorHandle(), topNCount, sortItems, assignments)
.map(result -> new TopNApplicationResult<>(
new TableHandle(catalogHandle, result.getHandle(), table.getTransaction()),
result.isTopNGuaranteed(),
result.isPrecalculateStatistics()));
}
@Override
public Optional> applyTableFunction(Session session, TableFunctionHandle handle)
{
CatalogHandle catalogHandle = handle.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
return metadata.applyTableFunction(session.toConnectorSession(catalogHandle), handle.getFunctionHandle())
.map(result -> new TableFunctionApplicationResult<>(
new TableHandle(catalogHandle, result.getTableHandle(), handle.getTransactionHandle()),
result.getColumnHandles()));
}
private void verifyProjection(TableHandle table, List projections, List assignments, int expectedProjectionSize)
{
projections.forEach(projection -> requireNonNull(projection, "one of the projections is null"));
assignments.forEach(assignment -> requireNonNull(assignment, "one of the assignments is null"));
verify(
expectedProjectionSize == projections.size(),
"ConnectorMetadata returned invalid number of projections: %s instead of %s for %s",
projections.size(),
expectedProjectionSize,
table);
Set assignedVariables = assignments.stream()
.map(Assignment::getVariable)
.collect(toImmutableSet());
projections.stream()
.flatMap(connectorExpression -> ConnectorExpressions.extractVariables(connectorExpression).stream())
.map(Variable::getName)
.filter(variableName -> !assignedVariables.contains(variableName))
.findAny()
.ifPresent(variableName -> { throw new IllegalStateException("Unbound variable: " + variableName); });
}
@Override
public void validateScan(Session session, TableHandle table)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
metadata.validateScan(session.toConnectorSession(catalogHandle), table.getConnectorHandle());
}
@Override
public Optional> applyFilter(Session session, TableHandle table, Constraint constraint)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyFilter(connectorSession, table.getConnectorHandle(), constraint)
.map(result -> result.transform(handle -> new TableHandle(catalogHandle, handle, table.getTransaction())));
}
@Override
public Optional> applyProjection(Session session, TableHandle table, List projections, Map assignments)
{
CatalogHandle catalogHandle = table.getCatalogHandle();
ConnectorMetadata metadata = getMetadata(session, catalogHandle);
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return metadata.applyProjection(connectorSession, table.getConnectorHandle(), projections, assignments)
.map(result -> {
verifyProjection(table, result.getProjections(), result.getAssignments(), projections.size());
return new ProjectionApplicationResult<>(
new TableHandle(catalogHandle, result.getHandle(), table.getTransaction()),
result.getProjections(),
result.getAssignments(),
result.isPrecalculateStatistics());
});
}
//
// Roles and Grants
//
@Override
public boolean isCatalogManagedSecurity(Session session, String catalog)
{
return getRequiredCatalogMetadata(session, catalog).getSecurityManagement() == CONNECTOR;
}
@Override
public boolean roleExists(Session session, String role, Optional catalog)
{
if (catalog.isEmpty()) {
return systemSecurityMetadata.roleExists(session, role);
}
CatalogMetadata catalogMetadata = getRequiredCatalogMetadata(session, catalog.get());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
return metadata.roleExists(session.toConnectorSession(catalogHandle), role);
}
@Override
public void createRole(Session session, String role, Optional grantor, Optional catalog)
{
if (catalog.isEmpty()) {
systemSecurityMetadata.createRole(session, role, grantor);
return;
}
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalog.get());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.createRole(session.toConnectorSession(catalogHandle), role, grantor);
}
@Override
public void dropRole(Session session, String role, Optional catalog)
{
if (catalog.isEmpty()) {
systemSecurityMetadata.dropRole(session, role);
return;
}
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalog.get());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.dropRole(session.toConnectorSession(catalogHandle), role);
}
@Override
public Set listRoles(Session session, Optional catalog)
{
if (catalog.isPresent()) {
Optional catalogMetadata = getOptionalCatalogMetadata(session, catalog.get());
if (catalogMetadata.isEmpty()) {
return ImmutableSet.of();
}
// If the connector is using system security management, we fall through to the system call
// instead of returning nothing, so information schema role tables will work properly
if (catalogMetadata.get().getSecurityManagement() == CONNECTOR) {
CatalogHandle catalogHandle = catalogMetadata.get().getCatalogHandle();
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ConnectorMetadata metadata = catalogMetadata.get().getMetadataFor(session, catalogHandle);
return metadata.listRoles(connectorSession).stream()
.map(role -> role.toLowerCase(ENGLISH))
.collect(toImmutableSet());
}
}
return systemSecurityMetadata.listRoles(session);
}
@Override
public Set listRoleGrants(Session session, Optional catalog, TrinoPrincipal principal)
{
if (catalog.isPresent()) {
Optional catalogMetadata = getOptionalCatalogMetadata(session, catalog.get());
if (catalogMetadata.isEmpty()) {
return ImmutableSet.of();
}
// If the connector is using system security management, we fall through to the system call
// instead of returning nothing, so information schema role tables will work properly
if (catalogMetadata.get().getSecurityManagement() == CONNECTOR) {
CatalogHandle catalogHandle = catalogMetadata.get().getCatalogHandle();
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ConnectorMetadata metadata = catalogMetadata.get().getMetadataFor(session, catalogHandle);
return metadata.listRoleGrants(connectorSession, principal);
}
}
return systemSecurityMetadata.listRoleGrants(session, principal);
}
@Override
public void grantRoles(Session session, Set roles, Set grantees, boolean adminOption, Optional grantor, Optional catalog)
{
if (catalog.isEmpty()) {
systemSecurityMetadata.grantRoles(session, roles, grantees, adminOption, grantor);
return;
}
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalog.get());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.grantRoles(session.toConnectorSession(catalogHandle), roles, grantees, adminOption, grantor);
}
@Override
public void revokeRoles(Session session, Set roles, Set grantees, boolean adminOption, Optional grantor, Optional catalog)
{
if (catalog.isEmpty()) {
systemSecurityMetadata.revokeRoles(session, roles, grantees, adminOption, grantor);
return;
}
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalog.get());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.revokeRoles(session.toConnectorSession(catalogHandle), roles, grantees, adminOption, grantor);
}
@Override
public Set listApplicableRoles(Session session, TrinoPrincipal principal, Optional catalog)
{
if (catalog.isPresent()) {
Optional catalogMetadata = getOptionalCatalogMetadata(session, catalog.get());
if (catalogMetadata.isEmpty()) {
return ImmutableSet.of();
}
// If the connector is using system security management, we fall through to the system call
// instead of returning nothing, so information schema role tables will work properly
if (catalogMetadata.get().getSecurityManagement() == CONNECTOR) {
CatalogHandle catalogHandle = catalogMetadata.get().getCatalogHandle();
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ConnectorMetadata metadata = catalogMetadata.get().getMetadataFor(session, catalogHandle);
return ImmutableSet.copyOf(metadata.listApplicableRoles(connectorSession, principal));
}
}
return systemSecurityMetadata.listApplicableRoles(session, principal);
}
@Override
public Set listEnabledRoles(Identity identity)
{
return systemSecurityMetadata.listEnabledRoles(identity);
}
@Override
public Set listEnabledRoles(Session session, String catalog)
{
Optional catalogMetadata = getOptionalCatalogMetadata(session, catalog);
if (catalogMetadata.isEmpty()) {
return ImmutableSet.of();
}
// If the connector is using system security management, we fall through to the system call
// instead of returning nothing, so information schema role tables will work properly
if (catalogMetadata.get().getSecurityManagement() == SYSTEM) {
return systemSecurityMetadata.listEnabledRoles(session.getIdentity());
}
CatalogHandle catalogHandle = catalogMetadata.get().getCatalogHandle();
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ConnectorMetadata metadata = catalogMetadata.get().getMetadataFor(session, catalogHandle);
return ImmutableSet.copyOf(metadata.listEnabledRoles(connectorSession));
}
@Override
public void grantTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, tableName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.grantTablePrivileges(session, tableName, privileges, grantee, grantOption);
return;
}
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.grantTablePrivileges(session.toConnectorSession(catalogHandle), tableName.asSchemaTableName(), privileges, grantee, grantOption);
}
@Override
public void denyTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, tableName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.denyTablePrivileges(session, tableName, privileges, grantee);
return;
}
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.denyTablePrivileges(session.toConnectorSession(catalogHandle), tableName.asSchemaTableName(), privileges, grantee);
}
@Override
public void revokeTablePrivileges(Session session, QualifiedObjectName tableName, Set privileges, TrinoPrincipal grantee, boolean grantOption)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, tableName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.revokeTablePrivileges(session, tableName, privileges, grantee, grantOption);
return;
}
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.revokeTablePrivileges(session.toConnectorSession(catalogHandle), tableName.asSchemaTableName(), privileges, grantee, grantOption);
}
@Override
public void grantSchemaPrivileges(Session session, CatalogSchemaName schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schemaName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.grantSchemaPrivileges(session, schemaName, privileges, grantee, grantOption);
return;
}
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.grantSchemaPrivileges(session.toConnectorSession(catalogHandle), schemaName.getSchemaName(), privileges, grantee, grantOption);
}
@Override
public void denySchemaPrivileges(Session session, CatalogSchemaName schemaName, Set privileges, TrinoPrincipal grantee)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schemaName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.denySchemaPrivileges(session, schemaName, privileges, grantee);
return;
}
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.denySchemaPrivileges(session.toConnectorSession(catalogHandle), schemaName.getSchemaName(), privileges, grantee);
}
@Override
public void revokeSchemaPrivileges(Session session, CatalogSchemaName schemaName, Set privileges, TrinoPrincipal grantee, boolean grantOption)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, schemaName.getCatalogName());
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
systemSecurityMetadata.revokeSchemaPrivileges(session, schemaName, privileges, grantee, grantOption);
return;
}
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.revokeSchemaPrivileges(session.toConnectorSession(catalogHandle), schemaName.getSchemaName(), privileges, grantee, grantOption);
}
// TODO support table redirection
@Override
public List listTablePrivileges(Session session, QualifiedTablePrefix prefix)
{
requireNonNull(prefix, "prefix is null");
Optional catalog = getOptionalCatalogMetadata(session, prefix.getCatalogName());
ImmutableSet.Builder grantInfos = ImmutableSet.builder();
if (catalog.isPresent()) {
CatalogMetadata catalogMetadata = catalog.get();
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
List catalogHandles = prefix.asQualifiedObjectName()
.map(qualifiedTableName -> singletonList(catalogMetadata.getCatalogHandle(session, qualifiedTableName)))
.orElseGet(catalogMetadata::listCatalogHandles);
for (CatalogHandle catalogHandle : catalogHandles) {
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
if (catalogMetadata.getSecurityManagement() == SYSTEM) {
grantInfos.addAll(systemSecurityMetadata.listTablePrivileges(session, prefix));
}
else {
grantInfos.addAll(metadata.listTablePrivileges(connectorSession, prefix.asSchemaTablePrefix()));
}
}
}
return ImmutableList.copyOf(grantInfos.build());
}
//
// Functions
//
@Override
public Collection listGlobalFunctions(Session session)
{
return functions.listFunctions();
}
@Override
public Collection listFunctions(Session session, CatalogSchemaName schema)
{
ImmutableList.Builder functions = ImmutableList.builder();
getOptionalCatalogMetadata(session, schema.getCatalogName()).ifPresent(catalogMetadata -> {
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
functions.addAll(metadata.listFunctions(connectorSession, schema.getSchemaName()));
functions.addAll(languageFunctionManager.listFunctions(metadata.listLanguageFunctions(connectorSession, schema.getSchemaName())));
});
return functions.build();
}
@Override
public ResolvedFunction decodeFunction(QualifiedName name)
{
return functionDecoder.fromQualifiedName(name)
.orElseThrow(() -> new IllegalArgumentException("Function is not resolved: " + name));
}
@Override
public ResolvedFunction resolveBuiltinFunction(String name, List parameterTypes)
{
return functionResolver.resolveBuiltinFunction(name, parameterTypes);
}
@Override
public ResolvedFunction resolveOperator(OperatorType operatorType, List extends Type> argumentTypes)
throws OperatorNotFoundException
{
return functionResolver.resolveOperator(operatorType, argumentTypes);
}
@Override
public ResolvedFunction getCoercion(OperatorType operatorType, Type fromType, Type toType)
{
return functionResolver.resolveCoercion(operatorType, fromType, toType);
}
@Override
public ResolvedFunction getCoercion(CatalogSchemaFunctionName name, Type fromType, Type toType)
{
// coercion can only be resolved for builtin functions
if (!isBuiltinFunctionName(name)) {
throw new TrinoException(FUNCTION_IMPLEMENTATION_MISSING, format("%s not found", name));
}
return functionResolver.resolveCoercion(name.getFunctionName(), fromType, toType);
}
@Override
public FunctionDependencyDeclaration getFunctionDependencies(Session session, CatalogHandle catalogHandle, FunctionId functionId, BoundSignature boundSignature)
{
if (isTrinoSqlLanguageFunction(functionId)) {
throw new IllegalArgumentException("Function dependencies for SQL functions must be fetched directly from the language manager");
}
if (catalogHandle.equals(GlobalSystemConnector.CATALOG_HANDLE)) {
return functions.getFunctionDependencies(functionId, boundSignature);
}
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
return getMetadata(session, catalogHandle)
.getFunctionDependencies(connectorSession, functionId, boundSignature);
}
@Override
public Collection getFunctions(Session session, CatalogSchemaFunctionName name)
{
if (isBuiltinFunctionName(name)) {
return getBuiltinFunctions(name.getFunctionName());
}
return getOptionalCatalogMetadata(session, name.getCatalogName())
.map(metadata -> getFunctions(session, metadata.getMetadata(session), metadata.getCatalogHandle(), name.getSchemaFunctionName()))
.orElse(ImmutableList.of());
}
private Collection getBuiltinFunctions(String functionName)
{
return functions.getBuiltInFunctions(functionName).stream()
.map(function -> new CatalogFunctionMetadata(GlobalSystemConnector.CATALOG_HANDLE, BUILTIN_SCHEMA, function))
.collect(toImmutableList());
}
private List getFunctions(Session session, ConnectorMetadata metadata, CatalogHandle catalogHandle, SchemaFunctionName name)
{
ConnectorSession connectorSession = session.toConnectorSession(catalogHandle);
ImmutableList.Builder functions = ImmutableList.builder();
metadata.getFunctions(connectorSession, name).stream()
.map(function -> new CatalogFunctionMetadata(catalogHandle, name.getSchemaName(), function))
.forEach(functions::add);
RunAsIdentityLoader identityLoader = owner -> {
CatalogSchemaFunctionName functionName = new CatalogSchemaFunctionName(catalogHandle.getCatalogName(), name);
Optional systemIdentity = Optional.empty();
if (getCatalogMetadata(session, catalogHandle).getSecurityManagement() == SYSTEM) {
systemIdentity = systemSecurityMetadata.getFunctionRunAsIdentity(session, functionName);
}
return systemIdentity.or(() -> owner.map(Identity::ofUser))
.orElseThrow(() -> new TrinoException(NOT_SUPPORTED, "No identity for SECURITY DEFINER function: " + functionName));
};
languageFunctionManager.getFunctions(session, catalogHandle, name, metadata::getLanguageFunctions, identityLoader).stream()
.map(function -> new CatalogFunctionMetadata(catalogHandle, name.getSchemaName(), function))
.forEach(functions::add);
return functions.build();
}
@Override
public AggregationFunctionMetadata getAggregationFunctionMetadata(Session session, ResolvedFunction resolvedFunction)
{
Signature functionSignature;
AggregationFunctionMetadata aggregationFunctionMetadata;
if (resolvedFunction.getCatalogHandle().equals(GlobalSystemConnector.CATALOG_HANDLE)) {
functionSignature = functions.getFunctionMetadata(resolvedFunction.getFunctionId()).getSignature();
aggregationFunctionMetadata = functions.getAggregationFunctionMetadata(resolvedFunction.getFunctionId());
}
else {
ConnectorSession connectorSession = session.toConnectorSession(resolvedFunction.getCatalogHandle());
ConnectorMetadata metadata = getMetadata(session, resolvedFunction.getCatalogHandle());
functionSignature = metadata.getFunctionMetadata(connectorSession, resolvedFunction.getFunctionId()).getSignature();
aggregationFunctionMetadata = metadata.getAggregationFunctionMetadata(connectorSession, resolvedFunction.getFunctionId());
}
AggregationFunctionMetadataBuilder builder = AggregationFunctionMetadata.builder();
if (aggregationFunctionMetadata.isOrderSensitive()) {
builder.orderSensitive();
}
if (!aggregationFunctionMetadata.getIntermediateTypes().isEmpty()) {
FunctionBinding functionBinding = toFunctionBinding(resolvedFunction.getFunctionId(), resolvedFunction.getSignature(), functionSignature);
aggregationFunctionMetadata.getIntermediateTypes().stream()
.map(typeSignature -> applyBoundVariables(typeSignature, functionBinding))
.forEach(builder::intermediateType);
}
return builder.build();
}
@Override
public boolean languageFunctionExists(Session session, QualifiedObjectName name, String signatureToken)
{
return getOptionalCatalogMetadata(session, name.getCatalogName())
.map(catalogMetadata -> {
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
return metadata.languageFunctionExists(connectorSession, name.asSchemaFunctionName(), signatureToken);
})
.orElse(false);
}
@Override
public void createLanguageFunction(Session session, QualifiedObjectName name, LanguageFunction function, boolean replace)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, name.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.createLanguageFunction(session.toConnectorSession(catalogHandle), name.asSchemaFunctionName(), function, replace);
}
@Override
public void dropLanguageFunction(Session session, QualifiedObjectName name, String signatureToken)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, name.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
ConnectorMetadata metadata = catalogMetadata.getMetadata(session);
metadata.dropLanguageFunction(session.toConnectorSession(catalogHandle), name.asSchemaFunctionName(), signatureToken);
}
@VisibleForTesting
public static FunctionBinding toFunctionBinding(FunctionId functionId, BoundSignature boundSignature, Signature functionSignature)
{
return SignatureBinder.bindFunction(
functionId,
functionSignature,
boundSignature);
}
//
// Helpers
//
private Optional getOptionalCatalogMetadata(Session session, String catalogName)
{
Optional optionalCatalogMetadata = transactionManager.getOptionalCatalogMetadata(session.getRequiredTransactionId(), catalogName);
optionalCatalogMetadata.ifPresent(catalogMetadata -> registerCatalogForQuery(session, catalogMetadata));
return optionalCatalogMetadata;
}
private CatalogMetadata getRequiredCatalogMetadata(Session session, String catalogName)
{
CatalogMetadata catalogMetadata = transactionManager.getRequiredCatalogMetadata(session.getRequiredTransactionId(), catalogName);
registerCatalogForQuery(session, catalogMetadata);
return catalogMetadata;
}
private CatalogMetadata getCatalogMetadata(Session session, CatalogHandle catalogHandle)
{
CatalogMetadata catalogMetadata = transactionManager.getCatalogMetadata(session.getRequiredTransactionId(), catalogHandle);
registerCatalogForQuery(session, catalogMetadata);
return catalogMetadata;
}
private CatalogMetadata getCatalogMetadataForWrite(Session session, String catalogName)
{
CatalogMetadata catalogMetadata = transactionManager.getCatalogMetadataForWrite(session.getRequiredTransactionId(), catalogName);
registerCatalogForQuery(session, catalogMetadata);
return catalogMetadata;
}
private CatalogMetadata getCatalogMetadataForWrite(Session session, CatalogHandle catalogHandle)
{
CatalogMetadata catalogMetadata = transactionManager.getCatalogMetadataForWrite(session.getRequiredTransactionId(), catalogHandle);
registerCatalogForQuery(session, catalogMetadata);
return catalogMetadata;
}
private ConnectorMetadata getMetadata(Session session, CatalogHandle catalogHandle)
{
return getCatalogMetadata(session, catalogHandle).getMetadataFor(session, catalogHandle);
}
private ConnectorMetadata getMetadataForWrite(Session session, CatalogHandle catalogHandle)
{
return getCatalogMetadataForWrite(session, catalogHandle).getMetadata(session);
}
private void registerCatalogForQuery(Session session, CatalogMetadata catalogMetadata)
{
catalogsByQueryId.computeIfAbsent(session.getQueryId(), queryId -> new QueryCatalogs(session))
.registerCatalog(catalogMetadata);
}
@VisibleForTesting
public Set getActiveQueryIds()
{
return ImmutableSet.copyOf(catalogsByQueryId.keySet());
}
private static class QueryCatalogs
{
private final Session session;
@GuardedBy("this")
private final Map catalogs = new HashMap<>();
@GuardedBy("this")
private boolean finished;
public QueryCatalogs(Session session)
{
this.session = requireNonNull(session, "session is null");
}
private synchronized void registerCatalog(CatalogMetadata catalogMetadata)
{
checkState(!finished, "Query is already finished");
if (catalogs.putIfAbsent(catalogMetadata.getCatalogHandle(), catalogMetadata) == null) {
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
catalogMetadata.getMetadata(session).beginQuery(connectorSession);
}
}
private synchronized void finish()
{
List catalogs;
synchronized (this) {
checkState(!finished, "Query is already finished");
finished = true;
catalogs = new ArrayList<>(this.catalogs.values());
}
for (CatalogMetadata catalogMetadata : catalogs) {
ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogHandle());
catalogMetadata.getMetadata(session).cleanupQuery(connectorSession);
}
}
}
@Override
public OptionalInt getMaxWriterTasks(Session session, String catalogName)
{
Optional catalog = getOptionalCatalogMetadata(session, catalogName);
if (catalog.isEmpty()) {
return OptionalInt.empty();
}
CatalogMetadata catalogMetadata = catalog.get();
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle();
return catalogMetadata.getMetadata(session).getMaxWriterTasks(session.toConnectorSession(catalogHandle));
}
@Override
public WriterScalingOptions getNewTableWriterScalingOptions(Session session, QualifiedObjectName tableName, Map tableProperties)
{
CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, tableName.getCatalogName());
CatalogHandle catalogHandle = catalogMetadata.getCatalogHandle(session, tableName);
ConnectorMetadata metadata = catalogMetadata.getMetadataFor(session, catalogHandle);
return metadata.getNewTableWriterScalingOptions(session.toConnectorSession(catalogHandle), tableName.asSchemaTableName(), tableProperties);
}
@Override
public WriterScalingOptions getInsertWriterScalingOptions(Session session, TableHandle tableHandle)
{
ConnectorMetadata metadata = getMetadataForWrite(session, tableHandle.getCatalogHandle());
return metadata.getInsertWriterScalingOptions(session.toConnectorSession(tableHandle.getCatalogHandle()), tableHandle.getConnectorHandle());
}
private Optional toConnectorVersion(Optional version)
{
Optional connectorVersion = Optional.empty();
if (version.isPresent()) {
connectorVersion = Optional.of(new ConnectorTableVersion(version.get().getPointerType(), version.get().getObjectType(), version.get().getPointer()));
}
return connectorVersion;
}
public static MetadataManager createTestMetadataManager()
{
return testMetadataManagerBuilder().build();
}
public static TestMetadataManagerBuilder testMetadataManagerBuilder()
{
return new TestMetadataManagerBuilder();
}
public static class TestMetadataManagerBuilder
{
private TransactionManager transactionManager;
private TypeManager typeManager = TESTING_TYPE_MANAGER;
private GlobalFunctionCatalog globalFunctionCatalog;
private LanguageFunctionManager languageFunctionManager;
private TestMetadataManagerBuilder() {}
public TestMetadataManagerBuilder withTransactionManager(TransactionManager transactionManager)
{
this.transactionManager = transactionManager;
return this;
}
public TestMetadataManagerBuilder withTypeManager(TypeManager typeManager)
{
this.typeManager = requireNonNull(typeManager, "typeManager is null");
return this;
}
public TestMetadataManagerBuilder withGlobalFunctionCatalog(GlobalFunctionCatalog globalFunctionCatalog)
{
this.globalFunctionCatalog = globalFunctionCatalog;
return this;
}
public TestMetadataManagerBuilder withLanguageFunctionManager(LanguageFunctionManager languageFunctionManager)
{
this.languageFunctionManager = languageFunctionManager;
return this;
}
public MetadataManager build()
{
TransactionManager transactionManager = this.transactionManager;
if (transactionManager == null) {
transactionManager = createTestTransactionManager();
}
GlobalFunctionCatalog globalFunctionCatalog = this.globalFunctionCatalog;
if (globalFunctionCatalog == null) {
globalFunctionCatalog = new GlobalFunctionCatalog();
TypeOperators typeOperators = new TypeOperators();
globalFunctionCatalog.addFunctions(SystemFunctionBundle.create(new FeaturesConfig(), typeOperators, new BlockTypeOperators(typeOperators), UNKNOWN));
globalFunctionCatalog.addFunctions(new InternalFunctionBundle(new LiteralFunction(new InternalBlockEncodingSerde(new BlockEncodingManager(), typeManager))));
}
if (languageFunctionManager == null) {
languageFunctionManager = new LanguageFunctionManager(new SqlParser(), typeManager, user -> ImmutableSet.of());
}
return new MetadataManager(
new DisabledSystemSecurityMetadata(),
transactionManager,
globalFunctionCatalog,
languageFunctionManager,
typeManager);
}
}
}