diff --git a/core/trino-main/src/main/java/io/trino/connector/informationschema/InformationSchemaMetadata.java b/core/trino-main/src/main/java/io/trino/connector/informationschema/InformationSchemaMetadata.java index 1c78f2d1efc3..f175d822a578 100644 --- a/core/trino-main/src/main/java/io/trino/connector/informationschema/InformationSchemaMetadata.java +++ b/core/trino-main/src/main/java/io/trino/connector/informationschema/InformationSchemaMetadata.java @@ -19,6 +19,7 @@ import io.airlift.slice.Slice; import io.trino.FullConnectorSession; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.QualifiedTablePrefix; @@ -353,7 +354,10 @@ private Set calculatePrefixesWithTableName( .flatMap(prefix -> tables.get().stream() .filter(this::isLowerCase) .map(table -> new QualifiedObjectName(catalogName, prefix.getSchemaName().get(), table))) - .filter(objectName -> !isColumnsEnumeratingTable(informationSchemaTable) || metadata.getTableHandle(session, objectName).isPresent() || metadata.getView(session, objectName).isPresent()) + .filter(objectName -> !isColumnsEnumeratingTable(informationSchemaTable) || + // TODO: Add a functional warning collector + metadata.getTableHandle(session, metadata.redirectTable(session, objectName, WarningCollector.NOOP)).isPresent() || + metadata.getView(session, metadata.redirectTable(session, objectName, WarningCollector.NOOP)).isPresent()) .filter(objectName -> predicate.isEmpty() || predicate.get().test(asFixedValues(objectName))) .map(QualifiedObjectName::asQualifiedTablePrefix) .collect(toImmutableSet()); diff --git a/core/trino-main/src/main/java/io/trino/connector/system/TableCommentSystemTable.java b/core/trino-main/src/main/java/io/trino/connector/system/TableCommentSystemTable.java index 90d1ac9737df..9f7887c9fbd3 100644 --- a/core/trino-main/src/main/java/io/trino/connector/system/TableCommentSystemTable.java +++ b/core/trino-main/src/main/java/io/trino/connector/system/TableCommentSystemTable.java @@ -17,6 +17,7 @@ import io.airlift.log.Logger; import io.trino.FullConnectorSession; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.QualifiedTablePrefix; @@ -105,7 +106,8 @@ public RecordCursor cursor(ConnectorTransactionHandle transactionHandle, Connect } for (SchemaTableName name : names) { - QualifiedObjectName tableName = new QualifiedObjectName(prefix.getCatalogName(), name.getSchemaName(), name.getTableName()); + // TODO: Add a functional warning collector + QualifiedObjectName tableName = metadata.redirectTable(session, new QualifiedObjectName(prefix.getCatalogName(), name.getSchemaName(), name.getTableName()), WarningCollector.NOOP); Optional comment = Optional.empty(); try { comment = metadata.getTableHandle(session, tableName) diff --git a/core/trino-main/src/main/java/io/trino/execution/AddColumnTask.java b/core/trino-main/src/main/java/io/trino/execution/AddColumnTask.java index 66c440bb779e..9b7c2a81a0f5 100644 --- a/core/trino-main/src/main/java/io/trino/execution/AddColumnTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/AddColumnTask.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -60,7 +61,14 @@ public String getName() } @Override - public ListenableFuture execute(AddColumn statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + AddColumn statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName()); diff --git a/core/trino-main/src/main/java/io/trino/execution/CallTask.java b/core/trino-main/src/main/java/io/trino/execution/CallTask.java index 508a83e471c7..534b23fd4877 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CallTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CallTask.java @@ -17,6 +17,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -73,7 +74,14 @@ public String getName() } @Override - public ListenableFuture execute(Call call, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Call call, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { if (!transactionManager.isAutoCommit(stateMachine.getSession().getRequiredTransactionId())) { throw new TrinoException(NOT_SUPPORTED, "Procedures cannot be called within a transaction (use autocommit mode)"); diff --git a/core/trino-main/src/main/java/io/trino/execution/CommentTask.java b/core/trino-main/src/main/java/io/trino/execution/CommentTask.java index 1b2da1f5b1f2..91b5eabd45a6 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CommentTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CommentTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -47,7 +48,14 @@ public String getName() } @Override - public ListenableFuture execute(Comment statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Comment statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); diff --git a/core/trino-main/src/main/java/io/trino/execution/CommitTask.java b/core/trino-main/src/main/java/io/trino/execution/CommitTask.java index 48489a66d115..8bb0c3980256 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CommitTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CommitTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -37,7 +38,14 @@ public String getName() } @Override - public ListenableFuture execute(Commit statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Commit statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); if (session.getTransactionId().isEmpty()) { diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java index a3a574b20a7f..f29be14dfec9 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateMaterializedViewTask.java @@ -17,6 +17,7 @@ import io.trino.Session; import io.trino.connector.CatalogName; import io.trino.cost.StatsCalculator; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -81,7 +82,8 @@ public ListenableFuture execute( Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, - List parameters) + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName name = createQualifiedObjectName(session, statement, statement.getName()); diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateRoleTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateRoleTask.java index 1b0d78486ac7..4f001f57ea55 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateRoleTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateRoleTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.security.TrinoPrincipal; @@ -45,7 +46,14 @@ public String getName() } @Override - public ListenableFuture execute(CreateRole statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + CreateRole statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); String catalog = getSessionCatalog(metadata, session, statement); diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateSchemaTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateSchemaTask.java index 88c87c6dd0b9..b349e9d34b4f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateSchemaTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateSchemaTask.java @@ -17,6 +17,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -59,7 +60,14 @@ public String explain(CreateSchema statement, List parameters) } @Override - public ListenableFuture execute(CreateSchema statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + CreateSchema statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { return internalExecute(statement, metadata, accessControl, stateMachine.getSession(), parameters); } diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateTableTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateTableTask.java index 5ab8b25b7788..774fdf5fefec 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateTableTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateTableTask.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -86,13 +87,20 @@ public String explain(CreateTable statement, List parameters) } @Override - public ListenableFuture execute(CreateTable statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + CreateTable statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { - return internalExecute(statement, metadata, accessControl, stateMachine.getSession(), parameters); + return internalExecute(statement, metadata, accessControl, stateMachine.getSession(), parameters, warningCollector); } @VisibleForTesting - ListenableFuture internalExecute(CreateTable statement, Metadata metadata, AccessControl accessControl, Session session, List parameters) + ListenableFuture internalExecute(CreateTable statement, Metadata metadata, AccessControl accessControl, Session session, List parameters, WarningCollector warningCollector) { checkArgument(!statement.getElements().isEmpty(), "no columns for table"); @@ -153,7 +161,7 @@ ListenableFuture internalExecute(CreateTable statement, Metadata metadata, Ac } else if (element instanceof LikeClause) { LikeClause likeClause = (LikeClause) element; - QualifiedObjectName likeTableName = createQualifiedObjectName(session, statement, likeClause.getTableName()); + QualifiedObjectName likeTableName = metadata.redirectTable(session, createQualifiedObjectName(session, statement, likeClause.getTableName()), warningCollector); if (metadata.getCatalogHandle(session, likeTableName.getCatalogName()).isEmpty()) { throw semanticException(CATALOG_NOT_FOUND, statement, "LIKE table catalog '%s' does not exist", likeTableName.getCatalogName()); } diff --git a/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java b/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java index 0d544ae7a2ad..0b516f128bdf 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CreateViewTask.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.cost.StatsCalculator; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -70,7 +71,14 @@ public String explain(CreateView statement, List parameters) } @Override - public ListenableFuture execute(CreateView statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + CreateView statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName name = createQualifiedObjectName(session, statement, statement.getName()); diff --git a/core/trino-main/src/main/java/io/trino/execution/DataDefinitionExecution.java b/core/trino-main/src/main/java/io/trino/execution/DataDefinitionExecution.java index 02c04b6c359d..b6ca104dea45 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DataDefinitionExecution.java +++ b/core/trino-main/src/main/java/io/trino/execution/DataDefinitionExecution.java @@ -59,6 +59,7 @@ public class DataDefinitionExecution private final AccessControl accessControl; private final QueryStateMachine stateMachine; private final List parameters; + private final WarningCollector warningCollector; private DataDefinitionExecution( DataDefinitionTask task, @@ -68,7 +69,8 @@ private DataDefinitionExecution( Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, - List parameters) + List parameters, + WarningCollector warningCollector) { this.task = requireNonNull(task, "task is null"); this.statement = requireNonNull(statement, "statement is null"); @@ -78,6 +80,7 @@ private DataDefinitionExecution( this.accessControl = requireNonNull(accessControl, "accessControl is null"); this.stateMachine = requireNonNull(stateMachine, "stateMachine is null"); this.parameters = parameters; + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -164,7 +167,7 @@ public void start() return; } - ListenableFuture future = task.execute(statement, transactionManager, metadata, accessControl, stateMachine, parameters); + ListenableFuture future = task.execute(statement, transactionManager, metadata, accessControl, stateMachine, parameters, warningCollector); Futures.addCallback(future, new FutureCallback() { @Override @@ -309,21 +312,22 @@ public DataDefinitionExecution createQueryExecution( Slug slug, WarningCollector warningCollector) { - return createDataDefinitionExecution(preparedQuery.getStatement(), preparedQuery.getParameters(), stateMachine, slug); + return createDataDefinitionExecution(preparedQuery.getStatement(), preparedQuery.getParameters(), stateMachine, slug, warningCollector); } private DataDefinitionExecution createDataDefinitionExecution( T statement, List parameters, QueryStateMachine stateMachine, - Slug slug) + Slug slug, + WarningCollector warningCollector) { @SuppressWarnings("unchecked") DataDefinitionTask task = (DataDefinitionTask) tasks.get(statement.getClass()); checkArgument(task != null, "no task for statement: %s", statement.getClass().getSimpleName()); stateMachine.setUpdateType(task.getName()); - return new DataDefinitionExecution<>(task, statement, slug, transactionManager, metadata, accessControl, stateMachine, parameters); + return new DataDefinitionExecution<>(task, statement, slug, transactionManager, metadata, accessControl, stateMachine, parameters, warningCollector); } } } diff --git a/core/trino-main/src/main/java/io/trino/execution/DataDefinitionTask.java b/core/trino-main/src/main/java/io/trino/execution/DataDefinitionTask.java index 679a06b8869e..dc36dc0348b9 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DataDefinitionTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DataDefinitionTask.java @@ -14,6 +14,7 @@ package io.trino.execution; import com.google.common.util.concurrent.ListenableFuture; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.sql.SqlFormatter; @@ -27,7 +28,14 @@ public interface DataDefinitionTask { String getName(); - ListenableFuture execute(T statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters); + ListenableFuture execute( + T statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector); default String explain(T statement, List parameters) { diff --git a/core/trino-main/src/main/java/io/trino/execution/DeallocateTask.java b/core/trino-main/src/main/java/io/trino/execution/DeallocateTask.java index a124f1f66ef2..36c467da66e4 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DeallocateTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DeallocateTask.java @@ -14,6 +14,7 @@ package io.trino.execution; import com.google.common.util.concurrent.ListenableFuture; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.sql.tree.Deallocate; @@ -34,7 +35,14 @@ public String getName() } @Override - public ListenableFuture execute(Deallocate statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Deallocate statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { String statementName = statement.getName().getValue(); stateMachine.removePreparedStatement(statementName); diff --git a/core/trino-main/src/main/java/io/trino/execution/DropColumnTask.java b/core/trino-main/src/main/java/io/trino/execution/DropColumnTask.java index 2c78900da723..08a373042c2a 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DropColumnTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DropColumnTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -45,7 +46,14 @@ public String getName() } @Override - public ListenableFuture execute(DropColumn statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + DropColumn statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getTable()); diff --git a/core/trino-main/src/main/java/io/trino/execution/DropMaterializedViewTask.java b/core/trino-main/src/main/java/io/trino/execution/DropMaterializedViewTask.java index fc3b8da675fe..45b11bcd1d41 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DropMaterializedViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DropMaterializedViewTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -47,7 +48,8 @@ public ListenableFuture execute( Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, - List parameters) + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName name = createQualifiedObjectName(session, statement, statement.getName()); diff --git a/core/trino-main/src/main/java/io/trino/execution/DropRoleTask.java b/core/trino-main/src/main/java/io/trino/execution/DropRoleTask.java index c8a0c1ded8af..b76973ca4b89 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DropRoleTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DropRoleTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.sql.tree.DropRole; @@ -40,7 +41,14 @@ public String getName() } @Override - public ListenableFuture execute(DropRole statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + DropRole statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); String catalog = getSessionCatalog(metadata, session, statement); diff --git a/core/trino-main/src/main/java/io/trino/execution/DropSchemaTask.java b/core/trino-main/src/main/java/io/trino/execution/DropSchemaTask.java index 32cbe3cbfe91..a2a2b334724b 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DropSchemaTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DropSchemaTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -48,7 +49,14 @@ public String explain(DropSchema statement, List parameters) } @Override - public ListenableFuture execute(DropSchema statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + DropSchema statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { if (statement.isCascade()) { throw new TrinoException(NOT_SUPPORTED, "CASCADE is not yet supported for DROP SCHEMA"); diff --git a/core/trino-main/src/main/java/io/trino/execution/DropTableTask.java b/core/trino-main/src/main/java/io/trino/execution/DropTableTask.java index 430b48a3e539..03eb1b47f9b4 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DropTableTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DropTableTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -41,7 +42,14 @@ public String getName() } @Override - public ListenableFuture execute(DropTable statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + DropTable statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getTableName()); diff --git a/core/trino-main/src/main/java/io/trino/execution/DropViewTask.java b/core/trino-main/src/main/java/io/trino/execution/DropViewTask.java index 124193663080..bff54c13ce0f 100644 --- a/core/trino-main/src/main/java/io/trino/execution/DropViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/DropViewTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -41,7 +42,14 @@ public String getName() } @Override - public ListenableFuture execute(DropView statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + DropView statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName name = createQualifiedObjectName(session, statement, statement.getName()); diff --git a/core/trino-main/src/main/java/io/trino/execution/GrantRolesTask.java b/core/trino-main/src/main/java/io/trino/execution/GrantRolesTask.java index bdb2e1766f4d..ce235b64b81d 100644 --- a/core/trino-main/src/main/java/io/trino/execution/GrantRolesTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/GrantRolesTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.MetadataUtil; import io.trino.security.AccessControl; @@ -47,7 +48,14 @@ public String getName() } @Override - public ListenableFuture execute(GrantRoles statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + GrantRoles statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); diff --git a/core/trino-main/src/main/java/io/trino/execution/GrantTask.java b/core/trino-main/src/main/java/io/trino/execution/GrantTask.java index b22e81410c29..d94bfe57c82b 100644 --- a/core/trino-main/src/main/java/io/trino/execution/GrantTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/GrantTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -51,7 +52,14 @@ public String getName() } @Override - public ListenableFuture execute(Grant statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Grant statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { if (statement.getType().filter(GrantOnType.SCHEMA::equals).isPresent()) { executeGrantOnSchema(stateMachine.getSession(), statement, metadata, accessControl); diff --git a/core/trino-main/src/main/java/io/trino/execution/PrepareTask.java b/core/trino-main/src/main/java/io/trino/execution/PrepareTask.java index 24c39b5112fe..8f152663e2f9 100644 --- a/core/trino-main/src/main/java/io/trino/execution/PrepareTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/PrepareTask.java @@ -14,6 +14,7 @@ package io.trino.execution; import com.google.common.util.concurrent.ListenableFuture; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -59,7 +60,14 @@ public String explain(Prepare statement, List parameters) } @Override - public ListenableFuture execute(Prepare prepare, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Prepare prepare, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Statement statement = prepare.getStatement(); if ((statement instanceof Prepare) || (statement instanceof Execute) || (statement instanceof Deallocate)) { diff --git a/core/trino-main/src/main/java/io/trino/execution/RenameColumnTask.java b/core/trino-main/src/main/java/io/trino/execution/RenameColumnTask.java index d365dbd2dafe..9dc7329e44d2 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RenameColumnTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RenameColumnTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -47,7 +48,14 @@ public String getName() } @Override - public ListenableFuture execute(RenameColumn statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + RenameColumn statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getTable()); diff --git a/core/trino-main/src/main/java/io/trino/execution/RenameSchemaTask.java b/core/trino-main/src/main/java/io/trino/execution/RenameSchemaTask.java index 31a882cd4524..8ba8e8af2a52 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RenameSchemaTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RenameSchemaTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.connector.CatalogSchemaName; @@ -41,7 +42,14 @@ public String getName() } @Override - public ListenableFuture execute(RenameSchema statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + RenameSchema statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); CatalogSchemaName source = createCatalogSchemaName(session, statement, Optional.of(statement.getSource())); diff --git a/core/trino-main/src/main/java/io/trino/execution/RenameTableTask.java b/core/trino-main/src/main/java/io/trino/execution/RenameTableTask.java index 85a59646ba6d..db47aef015d2 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RenameTableTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RenameTableTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -44,7 +45,14 @@ public String getName() } @Override - public ListenableFuture execute(RenameTable statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + RenameTable statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getSource()); diff --git a/core/trino-main/src/main/java/io/trino/execution/RenameViewTask.java b/core/trino-main/src/main/java/io/trino/execution/RenameViewTask.java index 4666074dbe26..d8cba2631988 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RenameViewTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RenameViewTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -44,7 +45,14 @@ public String getName() } @Override - public ListenableFuture execute(RenameView statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + RenameView statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName viewName = createQualifiedObjectName(session, statement, statement.getSource()); diff --git a/core/trino-main/src/main/java/io/trino/execution/ResetSessionTask.java b/core/trino-main/src/main/java/io/trino/execution/ResetSessionTask.java index 158d52de1b97..acc5626bb012 100644 --- a/core/trino-main/src/main/java/io/trino/execution/ResetSessionTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/ResetSessionTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.sql.tree.Expression; @@ -38,7 +39,14 @@ public String getName() } @Override - public ListenableFuture execute(ResetSession statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + ResetSession statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { List parts = statement.getName().getParts(); if (parts.size() > 2) { diff --git a/core/trino-main/src/main/java/io/trino/execution/RevokeRolesTask.java b/core/trino-main/src/main/java/io/trino/execution/RevokeRolesTask.java index cb3cd6ffce81..5766effc1516 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RevokeRolesTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RevokeRolesTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.MetadataUtil; import io.trino.security.AccessControl; @@ -47,7 +48,14 @@ public String getName() } @Override - public ListenableFuture execute(RevokeRoles statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + RevokeRoles statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); diff --git a/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java b/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java index 61fd8aae32d0..4ad306a91bdb 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RevokeTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.metadata.TableHandle; @@ -51,7 +52,14 @@ public String getName() } @Override - public ListenableFuture execute(Revoke statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Revoke statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { if (statement.getType().filter(GrantOnType.SCHEMA::equals).isPresent()) { executeRevokeOnSchema(stateMachine.getSession(), statement, metadata, accessControl); diff --git a/core/trino-main/src/main/java/io/trino/execution/RollbackTask.java b/core/trino-main/src/main/java/io/trino/execution/RollbackTask.java index 2fc9c8021d34..494cc8ef2d9d 100644 --- a/core/trino-main/src/main/java/io/trino/execution/RollbackTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/RollbackTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -38,7 +39,14 @@ public String getName() } @Override - public ListenableFuture execute(Rollback statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Rollback statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); if (session.getTransactionId().isEmpty()) { diff --git a/core/trino-main/src/main/java/io/trino/execution/SetPathTask.java b/core/trino-main/src/main/java/io/trino/execution/SetPathTask.java index a2ae820fac37..9de287986f4d 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetPathTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetPathTask.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.client.ClientCapabilities; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -51,7 +52,8 @@ public ListenableFuture execute( Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, - List parameters) + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); diff --git a/core/trino-main/src/main/java/io/trino/execution/SetRoleTask.java b/core/trino-main/src/main/java/io/trino/execution/SetRoleTask.java index e18b89f36f98..e6f1444a0fcb 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetRoleTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetRoleTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.security.SecurityContext; @@ -39,7 +40,14 @@ public String getName() } @Override - public ListenableFuture execute(SetRole statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + SetRole statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); String catalog = getSessionCatalog(metadata, session, statement); diff --git a/core/trino-main/src/main/java/io/trino/execution/SetSchemaAuthorizationTask.java b/core/trino-main/src/main/java/io/trino/execution/SetSchemaAuthorizationTask.java index 79de54af37eb..5cfda537ba10 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetSchemaAuthorizationTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetSchemaAuthorizationTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.connector.CatalogSchemaName; @@ -45,7 +46,14 @@ public String getName() } @Override - public ListenableFuture execute(SetSchemaAuthorization statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + SetSchemaAuthorization statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); String catalog = getSessionCatalog(metadata, session, statement); diff --git a/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java b/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java index c5b89676a101..535222c33f8b 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetSessionTask.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.security.SecurityContext; @@ -48,7 +49,14 @@ public String getName() } @Override - public ListenableFuture execute(SetSession statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + SetSession statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedName propertyName = statement.getName(); diff --git a/core/trino-main/src/main/java/io/trino/execution/SetTableAuthorizationTask.java b/core/trino-main/src/main/java/io/trino/execution/SetTableAuthorizationTask.java index e7de5d855bce..e4da1f9c9fae 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetTableAuthorizationTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetTableAuthorizationTask.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -46,7 +47,14 @@ public String getName() } @Override - public ListenableFuture execute(SetTableAuthorization statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + SetTableAuthorization statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getSource()); diff --git a/core/trino-main/src/main/java/io/trino/execution/SetViewAuthorizationTask.java b/core/trino-main/src/main/java/io/trino/execution/SetViewAuthorizationTask.java index f05f14eaffb5..5e87807450e8 100644 --- a/core/trino-main/src/main/java/io/trino/execution/SetViewAuthorizationTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/SetViewAuthorizationTask.java @@ -16,6 +16,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.metadata.QualifiedObjectName; import io.trino.security.AccessControl; @@ -46,7 +47,14 @@ public String getName() } @Override - public ListenableFuture execute(SetViewAuthorization statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + SetViewAuthorization statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); QualifiedObjectName viewName = createQualifiedObjectName(session, statement, statement.getSource()); diff --git a/core/trino-main/src/main/java/io/trino/execution/StartTransactionTask.java b/core/trino-main/src/main/java/io/trino/execution/StartTransactionTask.java index d13823681f30..e90b2c4ff62e 100644 --- a/core/trino-main/src/main/java/io/trino/execution/StartTransactionTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/StartTransactionTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.StandardErrorCode; @@ -44,7 +45,14 @@ public String getName() } @Override - public ListenableFuture execute(StartTransaction statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + StartTransaction statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); if (!session.isClientTransactionSupport()) { diff --git a/core/trino-main/src/main/java/io/trino/execution/UseTask.java b/core/trino-main/src/main/java/io/trino/execution/UseTask.java index 4c3c8355f98a..c9f2ea1259c0 100644 --- a/core/trino-main/src/main/java/io/trino/execution/UseTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/UseTask.java @@ -15,6 +15,7 @@ import com.google.common.util.concurrent.ListenableFuture; import io.trino.Session; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.Metadata; import io.trino.security.AccessControl; import io.trino.spi.TrinoException; @@ -41,7 +42,14 @@ public String getName() } @Override - public ListenableFuture execute(Use statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List parameters) + public ListenableFuture execute( + Use statement, + TransactionManager transactionManager, + Metadata metadata, + AccessControl accessControl, + QueryStateMachine stateMachine, + List parameters, + WarningCollector warningCollector) { Session session = stateMachine.getSession(); diff --git a/core/trino-main/src/main/java/io/trino/execution/warnings/DefaultWarningCollector.java b/core/trino-main/src/main/java/io/trino/execution/warnings/DefaultWarningCollector.java index 2acd87558b9f..5b5135cb2933 100644 --- a/core/trino-main/src/main/java/io/trino/execution/warnings/DefaultWarningCollector.java +++ b/core/trino-main/src/main/java/io/trino/execution/warnings/DefaultWarningCollector.java @@ -15,14 +15,13 @@ import com.google.common.collect.ImmutableList; import io.trino.spi.TrinoWarning; -import io.trino.spi.WarningCode; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; -import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; +import java.util.Set; import static java.util.Objects.requireNonNull; @@ -31,7 +30,7 @@ public class DefaultWarningCollector implements WarningCollector { @GuardedBy("this") - private final Map warnings = new LinkedHashMap<>(); + private final Set warnings = new LinkedHashSet<>(); private final WarningCollectorConfig config; public DefaultWarningCollector(WarningCollectorConfig config) @@ -44,13 +43,13 @@ public synchronized void add(TrinoWarning warning) { requireNonNull(warning, "warning is null"); if (warnings.size() < config.getMaxWarnings()) { - warnings.putIfAbsent(warning.getWarningCode(), warning); + warnings.add(warning); } } @Override public synchronized List getWarnings() { - return ImmutableList.copyOf(warnings.values()); + return ImmutableList.copyOf(warnings); } } diff --git a/core/trino-main/src/main/java/io/trino/metadata/Metadata.java b/core/trino-main/src/main/java/io/trino/metadata/Metadata.java index bf315e61bc94..a8bade3ea6f0 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/Metadata.java +++ b/core/trino-main/src/main/java/io/trino/metadata/Metadata.java @@ -16,6 +16,7 @@ import io.airlift.slice.Slice; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.operator.aggregation.InternalAggregationFunction; import io.trino.operator.window.WindowFunctionSupplier; import io.trino.spi.TrinoException; @@ -625,4 +626,9 @@ default ResolvedFunction getCoercion(Type fromType, Type toType) * This method is called after security checks against the original table. */ Optional applyTableScanRedirect(Session session, TableHandle tableHandle); + + /** + * Redirect to another table or view. Returns the original table name if the redirection doesn't happen. + */ + QualifiedObjectName redirectTable(Session session, QualifiedObjectName tableName, WarningCollector warningCollector); } diff --git a/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java b/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java index 33c400555731..e578a34e82c6 100644 --- a/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java +++ b/core/trino-main/src/main/java/io/trino/metadata/MetadataManager.java @@ -24,11 +24,13 @@ import io.trino.Session; import io.trino.client.NodeVersion; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.ResolvedFunction.ResolvedFunctionDecoder; import io.trino.operator.aggregation.InternalAggregationFunction; import io.trino.operator.window.WindowFunctionSupplier; import io.trino.spi.QueryId; import io.trino.spi.TrinoException; +import io.trino.spi.TrinoWarning; import io.trino.spi.block.ArrayBlockEncoding; import io.trino.spi.block.Block; import io.trino.spi.block.BlockEncoding; @@ -73,14 +75,16 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.Constraint; import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.GetViewsResult; import io.trino.spi.connector.JoinApplicationResult; import io.trino.spi.connector.JoinCondition; import io.trino.spi.connector.JoinType; import io.trino.spi.connector.LimitApplicationResult; +import io.trino.spi.connector.ListTableColumnsResult; +import io.trino.spi.connector.ListTablePrivilegesResult; import io.trino.spi.connector.MaterializedViewFreshness; import io.trino.spi.connector.ProjectionApplicationResult; import io.trino.spi.connector.SampleType; -import io.trino.spi.connector.SchemaTableName; import io.trino.spi.connector.SchemaTablePrefix; import io.trino.spi.connector.SortItem; import io.trino.spi.connector.SystemTable; @@ -137,6 +141,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Function; 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; @@ -156,7 +161,10 @@ 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_LIMIT; +import static io.trino.spi.StandardErrorCode.TABLE_REDIRECTION_LOOP; import static io.trino.spi.connector.ConnectorViewDefinition.ViewColumn; +import static io.trino.spi.connector.StandardWarningCode.TABLE_REDIRECTION; import static io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.BOXED_NULLABLE; import static io.trino.spi.function.InvocationConvention.InvocationArgumentConvention.NEVER_NULL; import static io.trino.spi.function.InvocationConvention.InvocationReturnConvention.FAIL_ON_NULL; @@ -199,6 +207,8 @@ public final class MetadataManager private final ResolvedFunctionDecoder functionDecoder; + private static final int MAX_TABLE_REDIRECTIONS = 100; + @Inject public MetadataManager( FeaturesConfig featuresConfig, @@ -557,8 +567,15 @@ public List listTables(Session session, QualifiedTablePrefi Optional objectName = prefix.asQualifiedObjectName(); if (objectName.isPresent()) { - if (isExistingRelation(session, objectName.get())) { - return ImmutableList.of(objectName.get()); + // Table cannot exist + if (objectName.get().getCatalogName().isEmpty() || objectName.get().getSchemaName().isEmpty() || objectName.get().getObjectName().isEmpty()) { + return ImmutableList.of(); + } + // Try to redirect the table. It's a no-op if redirection doesn't happen + // TODO: Add a functional warning collector + QualifiedObjectName tableName = redirectTable(session, objectName.get(), WarningCollector.NOOP); + if (isExistingRelation(session, tableName)) { + return ImmutableList.of(tableName); } return ImmutableList.of(); } @@ -604,27 +621,38 @@ public Map> listTableColumns(Session s ConnectorMetadata metadata = catalogMetadata.getMetadataFor(catalogName); ConnectorSession connectorSession = session.toConnectorSession(catalogName); - for (Entry> entry : metadata.listTableColumns(connectorSession, tablePrefix).entrySet()) { - QualifiedObjectName tableName = new QualifiedObjectName( - prefix.getCatalogName(), - entry.getKey().getSchemaName(), - entry.getKey().getTableName()); - tableColumns.put(tableName, entry.getValue()); - } - - // if table and view names overlap, the view wins - for (Entry entry : getViews(session, prefix).entrySet()) { - ImmutableList.Builder columns = ImmutableList.builder(); - for (ViewColumn column : entry.getValue().getColumns()) { - try { - columns.add(new ColumnMetadata(column.getName(), getType(column.getType()))); + try (Stream stream = metadata.listTableColumnsStream(connectorSession, tablePrefix)) { + stream.forEach(result -> { + QualifiedObjectName tableName = new QualifiedObjectName( + prefix.getCatalogName(), + result.getTableName().getSchemaName(), + result.getTableName().getTableName()); + if (result.getColumns().isPresent()) { + tableColumns.put(tableName, result.getColumns().get()); } - catch (TypeNotFoundException e) { - throw new TrinoException(INVALID_VIEW, format("Unknown type '%s' for column '%s' in view: %s", column.getType(), column.getName(), entry.getKey())); + else { + // Handle redirection + // TODO: Add a functional warning collector + QualifiedObjectName finalTable = redirectTable(session, tableName, WarningCollector.NOOP); + getTableHandle(session, finalTable).ifPresent(handle -> + tableColumns.put(tableName, getTableMetadata(session, handle).getColumns())); } + }); + } + } + + // if table and view names overlap, the view wins + for (Entry entry : getViews(session, prefix).entrySet()) { + ImmutableList.Builder columns = ImmutableList.builder(); + for (ViewColumn column : entry.getValue().getColumns()) { + try { + columns.add(new ColumnMetadata(column.getName(), getType(column.getType()))); + } + catch (TypeNotFoundException e) { + throw new TrinoException(INVALID_VIEW, format("Unknown type '%s' for column '%s' in view: %s", column.getType(), column.getName(), entry.getKey())); } - tableColumns.put(entry.getKey(), columns.build()); } + tableColumns.put(entry.getKey(), columns.build()); } } return ImmutableMap.copyOf(tableColumns); @@ -1024,7 +1052,13 @@ public List listViews(Session session, QualifiedTablePrefix Optional objectName = prefix.asQualifiedObjectName(); if (objectName.isPresent()) { - return getView(session, objectName.get()) + // View cannot exist + if (objectName.get().getCatalogName().isEmpty() || objectName.get().getSchemaName().isEmpty() || objectName.get().getObjectName().isEmpty()) { + return ImmutableList.of(); + } + // Try to redirect the view. It's a no-op if redirection doesn't happen + // TODO: Add a functional warning collector + return getView(session, redirectTable(session, objectName.get(), WarningCollector.NOOP)) .map(handle -> ImmutableList.of(objectName.get())) .orElseGet(ImmutableList::of); } @@ -1063,22 +1097,31 @@ public Map getViews(Session sessio ConnectorMetadata metadata = catalogMetadata.getMetadataFor(catalogName); ConnectorSession connectorSession = session.toConnectorSession(catalogName); - Map viewMap; if (tablePrefix.getTable().isPresent()) { - viewMap = metadata.getView(connectorSession, tablePrefix.toSchemaTableName()) - .map(view -> ImmutableMap.of(tablePrefix.toSchemaTableName(), view)) - .orElse(ImmutableMap.of()); + // Try to redirect the view. It's a no-op if redirection doesn't happen + // TODO: Add a functional warning collector + QualifiedObjectName viewName = prefix.asQualifiedObjectName().get(); + QualifiedObjectName finalView = redirectTable(session, viewName, WarningCollector.NOOP); + getView(session, finalView).ifPresent(viewDefinition -> views.put(viewName, viewDefinition)); } else { - viewMap = metadata.getViews(connectorSession, tablePrefix.getSchema()); - } - - for (Entry entry : viewMap.entrySet()) { - QualifiedObjectName viewName = new QualifiedObjectName( - prefix.getCatalogName(), - entry.getKey().getSchemaName(), - entry.getKey().getTableName()); - views.put(viewName, entry.getValue()); + try (Stream stream = metadata.getViewsStream(connectorSession, tablePrefix.getSchema())) { + stream.forEach(result -> { + QualifiedObjectName viewName = new QualifiedObjectName( + prefix.getCatalogName(), + result.getTableName().getSchemaName(), + result.getTableName().getTableName()); + if (result.getViewDefinition().isPresent()) { + views.put(viewName, result.getViewDefinition().get()); + } + else { + // Handle redirection + // TODO: Add a functional warning collector + QualifiedObjectName finalView = redirectTable(session, viewName, WarningCollector.NOOP); + getView(session, finalView).ifPresent(viewDefinition -> views.put(viewName, viewDefinition)); + } + }); + } } } } @@ -1641,11 +1684,37 @@ public List listTablePrivileges(Session session, QualifiedTablePrefix ConnectorSession connectorSession = session.toConnectorSession(catalogMetadata.getCatalogName()); List connectorIds = prefix.asQualifiedObjectName() + // Try to redirect the table. It's a no-op if redirection doesn't happen + // TODO: Add a functional warning collector + .map(qualifiedTableName -> redirectTable(session, qualifiedTableName, WarningCollector.NOOP)) .map(qualifiedTableName -> singletonList(catalogMetadata.getConnectorId(session, qualifiedTableName))) .orElseGet(catalogMetadata::listConnectorIds); for (CatalogName catalogName : connectorIds) { ConnectorMetadata metadata = catalogMetadata.getMetadataFor(catalogName); - grantInfos.addAll(metadata.listTablePrivileges(connectorSession, prefix.asSchemaTablePrefix())); + try (Stream stream = metadata.listTablePrivilegesStream(connectorSession, prefix.asSchemaTablePrefix())) { + stream.forEach(result -> { + QualifiedObjectName tableName = new QualifiedObjectName( + prefix.getCatalogName(), + result.getTableName().getSchemaName(), + result.getTableName().getTableName()); + if (result.getGrantInfo().isPresent()) { + grantInfos.add(result.getGrantInfo().get()); + } + else { + // Handle redirection + // TODO: Add a functional warning collector + QualifiedObjectName finalTable = redirectTable(session, tableName, WarningCollector.NOOP); + getOptionalCatalogMetadata(session, finalTable.getCatalogName()).ifPresent(finalCatalog -> { + ConnectorSession finalConnectorSession = session.toConnectorSession(finalCatalog.getCatalogName()); + ConnectorMetadata finalConnectorMetadata = finalCatalog.getMetadataFor(finalCatalog.getConnectorId(session, finalTable)); + SchemaTablePrefix finalSchemaTablePrefix = finalTable.asQualifiedTablePrefix().asSchemaTablePrefix(); + try (Stream privileges = finalConnectorMetadata.listTablePrivilegesStream(finalConnectorSession, finalSchemaTablePrefix)) { + privileges.forEach(res -> res.getGrantInfo().ifPresent(grantInfos::add)); + } + }); + } + }); + } } } return ImmutableList.copyOf(grantInfos.build()); @@ -2208,6 +2277,46 @@ public AnalyzePropertyManager getAnalyzePropertyManager() return analyzePropertyManager; } + @Override + public QualifiedObjectName redirectTable(Session session, QualifiedObjectName tableName, WarningCollector warningCollector) + { + requireNonNull(session, "session is null"); + requireNonNull(tableName, "tableName is null"); + requireNonNull(warningCollector, "warningCollector is null"); + + Set visitedTableNames = new LinkedHashSet<>(); + for (int count = 0; count < MAX_TABLE_REDIRECTIONS; count++) { + if (!visitedTableNames.add(tableName)) { + String redirectionChain = new StringBuilder() + .append(visitedTableNames.stream() + .map(QualifiedObjectName::toString) + .collect(Collectors.joining(" -> "))) + .append(" -> ") + .append(tableName) + .toString(); + throw new TrinoException(TABLE_REDIRECTION_LOOP, "Table redirections form a loop: " + redirectionChain); + } + Optional redirectedTableName = Optional.empty(); + Optional catalog = getOptionalCatalogMetadata(session, tableName.getCatalogName()); + if (catalog.isPresent()) { + CatalogMetadata catalogMetadata = catalog.get(); + CatalogName catalogName = catalogMetadata.getConnectorId(session, tableName); + ConnectorMetadata metadata = catalogMetadata.getMetadataFor(catalogName); + redirectedTableName = metadata.redirectTable(session.toConnectorSession(catalogName), tableName.asSchemaTableName()) + .map(name -> convertFromSchemaTableName(name.getCatalogName()).apply(name.getSchemaTableName())); + } + if (redirectedTableName.isEmpty()) { + return tableName; + } + warningCollector.add(new TrinoWarning(TABLE_REDIRECTION, format("Table or view '%s' redirected to '%s'", tableName, redirectedTableName.get()))); + tableName = redirectedTableName.get(); + } + String redirections = visitedTableNames.stream() + .map(QualifiedObjectName::toString) + .collect(Collectors.joining(" -> ")); + throw new TrinoException(TABLE_REDIRECTION_LIMIT, format("Too many table redirections (%d): %s", MAX_TABLE_REDIRECTIONS, redirections)); + } + // // Helpers // diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java index d58df339e8c8..67c98594f061 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java @@ -480,7 +480,7 @@ protected Scope visitInsert(Insert insert, Optional scope) @Override protected Scope visitRefreshMaterializedView(RefreshMaterializedView refreshMaterializedView, Optional scope) { - QualifiedObjectName name = createQualifiedObjectName(session, refreshMaterializedView, refreshMaterializedView.getName()); + QualifiedObjectName name = metadata.redirectTable(session, createQualifiedObjectName(session, refreshMaterializedView, refreshMaterializedView.getName()), warningCollector); Optional optionalView = metadata.getMaterializedView(session, name); if (optionalView.isEmpty()) { @@ -493,7 +493,7 @@ protected Scope visitRefreshMaterializedView(RefreshMaterializedView refreshMate throw semanticException(TABLE_NOT_FOUND, refreshMaterializedView, "Storage Table '%s' for materialized view '%s' does not exist", storageName, name); } - QualifiedObjectName targetTable = createQualifiedObjectName(session, refreshMaterializedView, storageName.get()); + QualifiedObjectName targetTable = metadata.redirectTable(session, createQualifiedObjectName(session, refreshMaterializedView, storageName.get()), warningCollector); // analyze the query that creates the data Query query = parseView(optionalView.get().getOriginalSql(), name, refreshMaterializedView); @@ -1206,7 +1206,7 @@ protected Scope visitTable(Table table, Optional scope) } } - QualifiedObjectName name = createQualifiedObjectName(session, table, table.getName()); + QualifiedObjectName name = metadata.redirectTable(session, createQualifiedObjectName(session, table, table.getName()), warningCollector); analysis.addEmptyColumnReferencesForTable(accessControl, session.getIdentity(), name); Optional tableHandle = Optional.empty(); @@ -1216,7 +1216,9 @@ protected Scope visitTable(Table table, Optional scope) // If materialized view is current, answer the query using the storage table Optional storageName = getMaterializedViewStorageTableName(name); if (storageName.isPresent()) { - tableHandle = metadata.getTableHandle(session, createQualifiedObjectName(session, table, storageName.get())); + tableHandle = metadata.getTableHandle( + session, + metadata.redirectTable(session, createQualifiedObjectName(session, table, storageName.get()), warningCollector)); } } else { diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ApplyTableScanRedirection.java b/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ApplyTableScanRedirection.java index 2f520faf4f7b..2275a3bf3024 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ApplyTableScanRedirection.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/iterative/rule/ApplyTableScanRedirection.java @@ -79,7 +79,7 @@ public Result apply(TableScanNode scanNode, Captures captures, Context context) CatalogSchemaTableName destinationTable = tableScanRedirectApplicationResult.get().getDestinationTable(); Optional destinationTableHandle = metadata.getTableHandle( context.getSession(), - convertFromSchemaTableName(destinationTable.getCatalogName()).apply(destinationTable.getSchemaTableName())); + metadata.redirectTable(context.getSession(), convertFromSchemaTableName(destinationTable.getCatalogName()).apply(destinationTable.getSchemaTableName()), context.getWarningCollector())); if (destinationTableHandle.isEmpty()) { throw new TrinoException(TABLE_NOT_FOUND, format("Destination table %s from table scan redirection not found", destinationTable)); } diff --git a/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java b/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java index 8ced30ac4aa0..a8ed197b8c9a 100644 --- a/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java +++ b/core/trino-main/src/main/java/io/trino/sql/rewrite/ShowQueriesRewrite.java @@ -164,7 +164,7 @@ public Statement rewrite( WarningCollector warningCollector, StatsCalculator statsCalculator) { - return (Statement) new Visitor(metadata, parser, session, accessControl).process(node, null); + return (Statement) new Visitor(metadata, parser, session, accessControl, warningCollector).process(node, null); } private static class Visitor @@ -174,13 +174,15 @@ private static class Visitor private final Session session; private final SqlParser sqlParser; private final AccessControl accessControl; + private final WarningCollector warningCollector; - public Visitor(Metadata metadata, SqlParser sqlParser, Session session, AccessControl accessControl) + public Visitor(Metadata metadata, SqlParser sqlParser, Session session, AccessControl accessControl, WarningCollector warningCollector) { this.metadata = requireNonNull(metadata, "metadata is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.session = requireNonNull(session, "session is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); + this.warningCollector = requireNonNull(warningCollector, "warningCollector is null"); } @Override @@ -236,11 +238,11 @@ protected Node visitShowGrants(ShowGrants showGrants, Void context) Optional tableName = showGrants.getTableName(); if (tableName.isPresent()) { - QualifiedObjectName qualifiedTableName = createQualifiedObjectName(session, showGrants, tableName.get()); + QualifiedObjectName qualifiedTableName = metadata.redirectTable(session, createQualifiedObjectName(session, showGrants, tableName.get()), warningCollector); if (metadata.getView(session, qualifiedTableName).isEmpty() && metadata.getTableHandle(session, qualifiedTableName).isEmpty()) { - throw semanticException(TABLE_NOT_FOUND, showGrants, "Table '%s' does not exist", tableName); + throw semanticException(TABLE_NOT_FOUND, showGrants, "Table '%s' does not exist", qualifiedTableName); } catalogName = qualifiedTableName.getCatalogName(); @@ -382,7 +384,7 @@ else if (node.getLikePattern().isPresent()) { @Override protected Node visitShowColumns(ShowColumns showColumns, Void context) { - QualifiedObjectName tableName = createQualifiedObjectName(session, showColumns, showColumns.getTable()); + QualifiedObjectName tableName = metadata.redirectTable(session, createQualifiedObjectName(session, showColumns, showColumns.getTable()), warningCollector); if (metadata.getCatalogHandle(session, tableName.getCatalogName()).isEmpty()) { throw semanticException(CATALOG_NOT_FOUND, showColumns, "Catalog '%s' does not exist", tableName.getCatalogName()); } @@ -458,7 +460,7 @@ private static Expression toExpression(Object value) protected Node visitShowCreate(ShowCreate node, Void context) { if (node.getType() == VIEW) { - QualifiedObjectName objectName = createQualifiedObjectName(session, node, node.getName()); + QualifiedObjectName objectName = metadata.redirectTable(session, createQualifiedObjectName(session, node, node.getName()), warningCollector); if (metadata.getMaterializedView(session, objectName).isPresent()) { throw semanticException(NOT_SUPPORTED, node, "Relation '%s' is a materialized view, not a view", objectName); @@ -479,7 +481,7 @@ protected Node visitShowCreate(ShowCreate node, Void context) Identifier schemaName = (parts.size() > 1) ? parts.get(1) : new Identifier(objectName.getSchemaName()); Identifier catalogName = (parts.size() > 2) ? parts.get(2) : new Identifier(objectName.getCatalogName()); - accessControl.checkCanShowCreateTable(session.toSecurityContext(), new QualifiedObjectName(catalogName.getValue(), schemaName.getValue(), tableName.getValue())); + accessControl.checkCanShowCreateTable(session.toSecurityContext(), objectName); CreateView.Security security = viewDefinition.get().isRunAsInvoker() ? INVOKER : DEFINER; String sql = formatSql(new CreateView(QualifiedName.of(ImmutableList.of(catalogName, schemaName, tableName)), query, false, viewDefinition.get().getComment(), Optional.of(security))).trim(); @@ -487,7 +489,7 @@ protected Node visitShowCreate(ShowCreate node, Void context) } if (node.getType() == MATERIALIZED_VIEW) { - QualifiedObjectName objectName = createQualifiedObjectName(session, node, node.getName()); + QualifiedObjectName objectName = metadata.redirectTable(session, createQualifiedObjectName(session, node, node.getName()), warningCollector); Optional viewDefinition = metadata.getMaterializedView(session, objectName); if (viewDefinition.isEmpty()) { @@ -503,7 +505,7 @@ protected Node visitShowCreate(ShowCreate node, Void context) Identifier schemaName = (parts.size() > 1) ? parts.get(1) : new Identifier(objectName.getSchemaName()); Identifier catalogName = (parts.size() > 2) ? parts.get(2) : new Identifier(objectName.getCatalogName()); - accessControl.checkCanShowCreateTable(session.toSecurityContext(), new QualifiedObjectName(catalogName.getValue(), schemaName.getValue(), tableName.getValue())); + accessControl.checkCanShowCreateTable(session.toSecurityContext(), objectName); String sql = formatSql(new CreateMaterializedView(Optional.empty(), QualifiedName.of(ImmutableList.of(catalogName, schemaName, tableName)), query, false, false, new ArrayList<>(), viewDefinition.get().getComment())).trim(); @@ -511,7 +513,7 @@ protected Node visitShowCreate(ShowCreate node, Void context) } if (node.getType() == TABLE) { - QualifiedObjectName objectName = createQualifiedObjectName(session, node, node.getName()); + QualifiedObjectName objectName = metadata.redirectTable(session, createQualifiedObjectName(session, node, node.getName()), warningCollector); Optional viewDefinition = metadata.getView(session, objectName); if (viewDefinition.isPresent()) { diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCallTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCallTask.java index 62d613855ba4..499dd15b61d8 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCallTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCallTask.java @@ -137,7 +137,8 @@ private void executeCallTask(MethodHandle methodHandle, Function getFutureValue(new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()))) + () -> getFutureValue(new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP))) .hasErrorCode(NOT_IN_TRANSACTION); assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); @@ -109,7 +109,7 @@ public void testUnknownTransactionCommit() .build(); QueryStateMachine stateMachine = createQueryStateMachine("COMMIT", session, transactionManager); - Future future = new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()); + Future future = new CommitTask().execute(new Commit(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP); assertTrinoExceptionThrownBy(() -> getFutureValue(future)) .hasErrorCode(UNKNOWN_TRANSACTION); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestCreateTableTask.java b/core/trino-main/src/test/java/io/trino/execution/TestCreateTableTask.java index 73435676afc1..c2a117e53a82 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestCreateTableTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestCreateTableTask.java @@ -19,6 +19,7 @@ import io.trino.connector.CatalogName; import io.trino.eventlistener.EventListenerConfig; import io.trino.eventlistener.EventListenerManager; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.AbstractMockMetadata; import io.trino.metadata.Catalog; import io.trino.metadata.CatalogManager; @@ -135,7 +136,7 @@ public void testCreateTableNotExistsTrue() ImmutableList.of(), Optional.empty()); - getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList())); + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList(), WarningCollector.NOOP)); assertEquals(metadata.getCreateTableCallCount(), 1); } @@ -149,7 +150,7 @@ public void testCreateTableNotExistsFalse() Optional.empty()); try { - getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList())); + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList(), WarningCollector.NOOP)); fail("expected exception"); } catch (RuntimeException e) { @@ -171,7 +172,7 @@ public void testCreateWithNotNullColumns() new ColumnDefinition(identifier("c"), toSqlType(VARBINARY), false, emptyList(), Optional.empty())); CreateTable statement = new CreateTable(QualifiedName.of("test_table"), inputColumns, true, ImmutableList.of(), Optional.empty()); - getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList())); + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList(), WarningCollector.NOOP)); assertEquals(metadata.getCreateTableCallCount(), 1); List columns = metadata.getReceivedTableMetadata().get(0).getColumns(); assertEquals(columns.size(), 3); @@ -204,7 +205,7 @@ public void testCreateWithUnsupportedConnectorThrowsWhenNotNull() Optional.empty()); assertTrinoExceptionThrownBy(() -> - getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList()))) + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, emptyList(), WarningCollector.NOOP))) .hasErrorCode(NOT_SUPPORTED) .hasMessage("Catalog 'catalog' does not support non-null column for column name 'b'"); } @@ -214,7 +215,7 @@ public void testCreateLike() { CreateTable statement = getCreatleLikeStatement(false); - getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, List.of())); + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, List.of(), WarningCollector.NOOP)); assertEquals(metadata.getCreateTableCallCount(), 1); assertThat(metadata.getReceivedTableMetadata().get(0).getColumns()) @@ -227,7 +228,7 @@ public void testCreateLikeWithProperties() { CreateTable statement = getCreatleLikeStatement(true); - getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, List.of())); + getFutureValue(new CreateTableTask().internalExecute(statement, metadata, new AllowAllAccessControl(), testSession, List.of(), WarningCollector.NOOP)); assertEquals(metadata.getCreateTableCallCount(), 1); assertThat(metadata.getReceivedTableMetadata().get(0).getColumns()) @@ -244,7 +245,7 @@ public void testCreateLikeDenyPermission() TestingAccessControlManager accessControl = new TestingAccessControlManager(transactionManager, new EventListenerManager(new EventListenerConfig())); accessControl.deny(privilege("parent_table", SELECT_COLUMN)); - assertThatThrownBy(() -> getFutureValue(new CreateTableTask().internalExecute(statement, metadata, accessControl, testSession, List.of()))) + assertThatThrownBy(() -> getFutureValue(new CreateTableTask().internalExecute(statement, metadata, accessControl, testSession, List.of(), WarningCollector.NOOP))) .isInstanceOf(AccessDeniedException.class) .hasMessageContaining("Cannot reference columns of table"); } @@ -257,7 +258,7 @@ public void testCreateLikeWithPropertiesDenyPermission() TestingAccessControlManager accessControl = new TestingAccessControlManager(transactionManager, new EventListenerManager(new EventListenerConfig())); accessControl.deny(privilege("parent_table", SHOW_CREATE_TABLE)); - assertThatThrownBy(() -> getFutureValue(new CreateTableTask().internalExecute(statement, metadata, accessControl, testSession, List.of()))) + assertThatThrownBy(() -> getFutureValue(new CreateTableTask().internalExecute(statement, metadata, accessControl, testSession, List.of(), WarningCollector.NOOP))) .isInstanceOf(AccessDeniedException.class) .hasMessageContaining("Cannot reference properties of table"); } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestDeallocateTask.java b/core/trino-main/src/test/java/io/trino/execution/TestDeallocateTask.java index 3f60529c9c6e..9fa0b43f8589 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestDeallocateTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestDeallocateTask.java @@ -95,7 +95,7 @@ private Set executeDeallocate(String statementName, String sqlString, Se WarningCollector.NOOP, Optional.empty()); Deallocate deallocate = new Deallocate(new Identifier(statementName)); - new DeallocateTask().execute(deallocate, transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()); + new DeallocateTask().execute(deallocate, transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP); return stateMachine.getDeallocatedPreparedStatements(); } } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestPrepareTask.java b/core/trino-main/src/test/java/io/trino/execution/TestPrepareTask.java index 63fe6ed23454..d671b9cbc69b 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestPrepareTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestPrepareTask.java @@ -118,7 +118,7 @@ private Map executePrepare(String statementName, Statement state WarningCollector.NOOP, Optional.empty()); Prepare prepare = new Prepare(identifier(statementName), statement); - new PrepareTask(new SqlParser()).execute(prepare, transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()); + new PrepareTask(new SqlParser()).execute(prepare, transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP); return stateMachine.getAddedPreparedStatements(); } } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestResetSessionTask.java b/core/trino-main/src/test/java/io/trino/execution/TestResetSessionTask.java index 7236d8aedec4..18e149967833 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestResetSessionTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestResetSessionTask.java @@ -112,7 +112,8 @@ public void test() metadata, accessControl, stateMachine, - emptyList())); + emptyList(), + WarningCollector.NOOP)); Set sessionProperties = stateMachine.getResetSessionProperties(); assertEquals(sessionProperties, ImmutableSet.of("catalog.baz")); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRollbackTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRollbackTask.java index 0f7f6a0a0ae9..10e61ed8e263 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestRollbackTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestRollbackTask.java @@ -69,7 +69,7 @@ public void testRollback() assertTrue(stateMachine.getSession().getTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); - getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); + getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP)); assertTrue(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); @@ -86,7 +86,7 @@ public void testNoTransactionRollback() QueryStateMachine stateMachine = createQueryStateMachine("ROLLBACK", session, transactionManager); assertTrinoExceptionThrownBy( - () -> getFutureValue((Future) new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()))) + () -> getFutureValue((Future) new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP))) .hasErrorCode(NOT_IN_TRANSACTION); assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); @@ -105,7 +105,7 @@ public void testUnknownTransactionRollback() .build(); QueryStateMachine stateMachine = createQueryStateMachine("ROLLBACK", session, transactionManager); - getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); + getFutureValue(new RollbackTask().execute(new Rollback(), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP)); assertTrue(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); // Still issue clear signal assertFalse(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSetPathTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSetPathTask.java index b3d9344d1386..a948aae7a59d 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSetPathTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSetPathTask.java @@ -114,6 +114,7 @@ private void executeSetPathTask(PathSpecification pathSpecification, QueryStateM metadata, accessControl, stateMachine, - emptyList())); + emptyList(), + WarningCollector.NOOP)); } } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSetRoleTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSetRoleTask.java index a1df9fbb8e86..0713bd8a5f2a 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSetRoleTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSetRoleTask.java @@ -122,7 +122,7 @@ private QueryStateMachine executeSetRole(String catalog, String statement) metadata, WarningCollector.NOOP, Optional.empty()); - new SetRoleTask().execute(setRole, transactionManager, metadata, accessControl, stateMachine, ImmutableList.of()); + new SetRoleTask().execute(setRole, transactionManager, metadata, accessControl, stateMachine, ImmutableList.of(), WarningCollector.NOOP); return stateMachine; } } diff --git a/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java b/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java index fca007a824fb..6e6e2d4afd1a 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestSetSessionTask.java @@ -198,7 +198,7 @@ private void testSetSessionWithParameters(String property, Expression expression metadata, WarningCollector.NOOP, Optional.empty()); - getFutureValue(new SetSessionTask().execute(new SetSession(qualifiedPropName, expression), transactionManager, metadata, accessControl, stateMachine, parameters)); + getFutureValue(new SetSessionTask().execute(new SetSession(qualifiedPropName, expression), transactionManager, metadata, accessControl, stateMachine, parameters, WarningCollector.NOOP)); Map sessionProperties = stateMachine.getSetSessionProperties(); assertEquals(sessionProperties, ImmutableMap.of(qualifiedPropName.toString(), expectedValue)); diff --git a/core/trino-main/src/test/java/io/trino/execution/TestStartTransactionTask.java b/core/trino-main/src/test/java/io/trino/execution/TestStartTransactionTask.java index d8f1f052d500..6ea19df9cfe6 100644 --- a/core/trino-main/src/test/java/io/trino/execution/TestStartTransactionTask.java +++ b/core/trino-main/src/test/java/io/trino/execution/TestStartTransactionTask.java @@ -84,7 +84,7 @@ public void testNonTransactionalClient() assertFalse(stateMachine.getSession().getTransactionId().isPresent()); assertTrinoExceptionThrownBy( - () -> getFutureValue((Future) new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()))) + () -> getFutureValue((Future) new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP))) .hasErrorCode(INCOMPATIBLE_CLIENT); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); @@ -104,7 +104,7 @@ public void testNestedTransaction() QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertTrinoExceptionThrownBy( - () -> getFutureValue((Future) new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList()))) + () -> getFutureValue((Future) new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP))) .hasErrorCode(NOT_SUPPORTED); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); @@ -123,7 +123,7 @@ public void testStartTransaction() QueryStateMachine stateMachine = createQueryStateMachine("START TRANSACTION", session, transactionManager); assertFalse(stateMachine.getSession().getTransactionId().isPresent()); - getFutureValue(new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList())); + getFutureValue(new StartTransactionTask().execute(new StartTransaction(ImmutableList.of()), transactionManager, metadata, new AllowAllAccessControl(), stateMachine, emptyList(), WarningCollector.NOOP)); assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); assertTrue(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); @@ -148,7 +148,8 @@ public void testStartTransactionExplicitModes() metadata, new AllowAllAccessControl(), stateMachine, - emptyList())); + emptyList(), + WarningCollector.NOOP)); assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); assertTrue(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); assertEquals(transactionManager.getAllTransactionInfos().size(), 1); @@ -176,7 +177,8 @@ public void testStartTransactionTooManyIsolationLevels() metadata, new AllowAllAccessControl(), stateMachine, - emptyList()))) + emptyList(), + WarningCollector.NOOP))) .hasErrorCode(SYNTAX_ERROR); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); @@ -202,7 +204,8 @@ public void testStartTransactionTooManyAccessModes() metadata, new AllowAllAccessControl(), stateMachine, - emptyList()))) + emptyList(), + WarningCollector.NOOP))) .hasErrorCode(SYNTAX_ERROR); assertTrue(transactionManager.getAllTransactionInfos().isEmpty()); @@ -234,7 +237,8 @@ public void testStartTransactionIdleExpiration() metadata, new AllowAllAccessControl(), stateMachine, - emptyList())); + emptyList(), + WarningCollector.NOOP)); assertFalse(stateMachine.getQueryInfo(Optional.empty()).isClearTransactionId()); assertTrue(stateMachine.getQueryInfo(Optional.empty()).getStartedTransactionId().isPresent()); diff --git a/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java b/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java index bfe31765d56b..1ae7c4414f4f 100644 --- a/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java +++ b/core/trino-main/src/test/java/io/trino/metadata/AbstractMockMetadata.java @@ -20,6 +20,7 @@ import io.airlift.slice.Slice; import io.trino.Session; import io.trino.connector.CatalogName; +import io.trino.execution.warnings.WarningCollector; import io.trino.metadata.ResolvedFunction.ResolvedFunctionDecoder; import io.trino.operator.aggregation.InternalAggregationFunction; import io.trino.operator.window.WindowFunctionSupplier; @@ -852,4 +853,11 @@ public Optional applyTableScanRedirect(Sessi { throw new UnsupportedOperationException(); } + + @Override + public QualifiedObjectName redirectTable(Session session, QualifiedObjectName tableName, WarningCollector warningCollector) + { + // no-op + return tableName; + } } diff --git a/core/trino-spi/src/main/java/io/trino/spi/StandardErrorCode.java b/core/trino-spi/src/main/java/io/trino/spi/StandardErrorCode.java index 001975266f20..7e162728c044 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/StandardErrorCode.java +++ b/core/trino-spi/src/main/java/io/trino/spi/StandardErrorCode.java @@ -143,6 +143,8 @@ public enum StandardErrorCode CONFIGURATION_UNAVAILABLE(65560, INTERNAL_ERROR), INVALID_RESOURCE_GROUP(65561, INTERNAL_ERROR), SERIALIZATION_ERROR(65562, INTERNAL_ERROR), + TABLE_REDIRECTION_LOOP(65563, INTERNAL_ERROR), + TABLE_REDIRECTION_LIMIT(65564, INTERNAL_ERROR), GENERIC_INSUFFICIENT_RESOURCES(131072, INSUFFICIENT_RESOURCES), EXCEEDED_GLOBAL_MEMORY_LIMIT(131073, INSUFFICIENT_RESOURCES), diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java index 16038c25ce5b..09af0af5d690 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ConnectorMetadata.java @@ -37,6 +37,7 @@ import java.util.OptionalLong; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import static io.trino.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED; @@ -223,6 +224,15 @@ default Map> listTableColumns(ConnectorSes return emptyMap(); } + /** + * Gets the metadata for all columns that match the specified table prefix. For redirected tables, returns a {@link ListTableColumnsResult} + * with an Optional.empty() field value in the stream. + */ + default Stream listTableColumnsStream(ConnectorSession session, SchemaTablePrefix prefix) + { + return listTableColumns(session, prefix).entrySet().stream().map(entry -> new ListTableColumnsResult(entry.getKey(), Optional.of(entry.getValue()))); + } + /** * Get statistics for table for given filtering constraint. */ @@ -597,6 +607,15 @@ default Map getViews(ConnectorSession return views; } + /** + * Gets the definitions of views, possibly filtered by schema. For redirected tables, returns a {@link GetViewsResult} + * with an Optional.empty() field value in the stream. + */ + default Stream getViewsStream(ConnectorSession session, Optional schemaName) + { + return getViews(session, schemaName).entrySet().stream().map(entry -> new GetViewsResult(entry.getKey(), Optional.of(entry.getValue()))); + } + /** * Gets the view data for the specified view name. */ @@ -785,6 +804,15 @@ default List listTablePrivileges(ConnectorSession session, SchemaTabl return emptyList(); } + /** + * List the table privileges granted to the specified grantee for the tables that have the specified prefix considering the selected session role. + * For redirected tables, returns a {@link ListTablePrivilegesResult} with an Optional.empty() field value in the stream. + */ + default Stream listTablePrivilegesStream(ConnectorSession session, SchemaTablePrefix prefix) + { + return listTablePrivileges(session, prefix).stream().map(grantInfo -> new ListTablePrivilegesResult(grantInfo.getSchemaTableName(), Optional.of(grantInfo))); + } + /** * Whether the connector uses the legacy Table Layout feature. If this method returns false, * connectors are required to implement the following methods: @@ -1129,4 +1157,15 @@ default Optional applyTableScanRedirect(Conn { return Optional.empty(); } + + /** + * Redirects table reads to other tables or views which may or may not be in the same catalog. + * + * Currently the engine tries to do redirection only for table reads and metadata listing. For metadata listing to work properly, you should + * consider implementing {@link #listTableColumnsStream}, {@link #getViewsStream}, and {@link #listTablePrivilegesStream}. + */ + default Optional redirectTable(ConnectorSession session, SchemaTableName tableName) + { + return Optional.empty(); + } } diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/GetViewsResult.java b/core/trino-spi/src/main/java/io/trino/spi/connector/GetViewsResult.java new file mode 100644 index 000000000000..7d9acc75d292 --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/GetViewsResult.java @@ -0,0 +1,40 @@ +/* + * 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.spi.connector; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class GetViewsResult +{ + private final SchemaTableName tableName; + private final Optional viewDefinition; // An Optional.empty() value means the table is redirected + + public GetViewsResult(SchemaTableName tableName, Optional viewDefinition) + { + this.tableName = requireNonNull(tableName, "tableName is null"); + this.viewDefinition = requireNonNull(viewDefinition, "viewDefinition is null"); + } + + public SchemaTableName getTableName() + { + return tableName; + } + + public Optional getViewDefinition() + { + return viewDefinition; + } +} diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ListTableColumnsResult.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ListTableColumnsResult.java new file mode 100644 index 000000000000..c31bc85ff0c5 --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ListTableColumnsResult.java @@ -0,0 +1,41 @@ +/* + * 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.spi.connector; + +import java.util.List; +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ListTableColumnsResult +{ + private final SchemaTableName tableName; + private final Optional> columns; // An Optional.empty() value means the table is redirected + + public ListTableColumnsResult(SchemaTableName tableName, Optional> columns) + { + this.tableName = requireNonNull(tableName, "tableName is null"); + this.columns = requireNonNull(columns, "columns is null"); + } + + public SchemaTableName getTableName() + { + return tableName; + } + + public Optional> getColumns() + { + return columns; + } +} diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/ListTablePrivilegesResult.java b/core/trino-spi/src/main/java/io/trino/spi/connector/ListTablePrivilegesResult.java new file mode 100644 index 000000000000..40e2d42afc81 --- /dev/null +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/ListTablePrivilegesResult.java @@ -0,0 +1,42 @@ +/* + * 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.spi.connector; + +import io.trino.spi.security.GrantInfo; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class ListTablePrivilegesResult +{ + private final SchemaTableName tableName; + private final Optional grantInfo; // An Optional.empty() value means the table is redirected + + public ListTablePrivilegesResult(SchemaTableName tableName, Optional grantInfo) + { + this.tableName = requireNonNull(tableName, "tableName is null"); + this.grantInfo = requireNonNull(grantInfo, "grantInfo is null"); + } + + public SchemaTableName getTableName() + { + return tableName; + } + + public Optional getGrantInfo() + { + return grantInfo; + } +} diff --git a/core/trino-spi/src/main/java/io/trino/spi/connector/StandardWarningCode.java b/core/trino-spi/src/main/java/io/trino/spi/connector/StandardWarningCode.java index df9e44c957f0..74df73a66c37 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/connector/StandardWarningCode.java +++ b/core/trino-spi/src/main/java/io/trino/spi/connector/StandardWarningCode.java @@ -22,6 +22,7 @@ public enum StandardWarningCode TOO_MANY_STAGES(0x0000_0001), REDUNDANT_ORDER_BY(0x0000_0002), DEPRECATED_FUNCTION(0x0000_0003), + TABLE_REDIRECTION(0x0000_0004), /**/; private final WarningCode warningCode; diff --git a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java index 95801f88dec2..158c74a566f0 100644 --- a/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java +++ b/lib/trino-plugin-toolkit/src/main/java/io/trino/plugin/base/classloader/ClassLoaderSafeConnectorMetadata.java @@ -18,6 +18,7 @@ import io.trino.spi.connector.AggregateFunction; import io.trino.spi.connector.AggregationApplicationResult; 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.ConnectorInsertTableHandle; @@ -38,10 +39,13 @@ import io.trino.spi.connector.ConnectorViewDefinition; import io.trino.spi.connector.Constraint; import io.trino.spi.connector.ConstraintApplicationResult; +import io.trino.spi.connector.GetViewsResult; import io.trino.spi.connector.JoinApplicationResult; import io.trino.spi.connector.JoinCondition; import io.trino.spi.connector.JoinType; import io.trino.spi.connector.LimitApplicationResult; +import io.trino.spi.connector.ListTableColumnsResult; +import io.trino.spi.connector.ListTablePrivilegesResult; import io.trino.spi.connector.MaterializedViewFreshness; import io.trino.spi.connector.ProjectionApplicationResult; import io.trino.spi.connector.SampleType; @@ -69,6 +73,7 @@ import java.util.Optional; import java.util.OptionalLong; import java.util.Set; +import java.util.stream.Stream; import static java.util.Objects.requireNonNull; @@ -273,6 +278,14 @@ public Map> listTableColumns(ConnectorSess } } + @Override + public Stream listTableColumnsStream(ConnectorSession session, SchemaTablePrefix prefix) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.listTableColumnsStream(session, prefix); + } + } + @Override public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTableHandle tableHandle, Constraint constraint) { @@ -527,6 +540,14 @@ public Map getViews(ConnectorSession s } } + @Override + public Stream getViewsStream(ConnectorSession session, Optional schemaName) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.getViewsStream(session, schemaName); + } + } + @Override public Optional getView(ConnectorSession session, SchemaTableName viewName) { @@ -727,6 +748,14 @@ public List listTablePrivileges(ConnectorSession session, SchemaTable } } + @Override + public Stream listTablePrivilegesStream(ConnectorSession session, SchemaTablePrefix prefix) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.listTablePrivilegesStream(session, prefix); + } + } + @Override public boolean usesLegacyTableLayouts() { @@ -879,4 +908,12 @@ public void finishUpdate(ConnectorSession session, ConnectorTableHandle tableHan delegate.finishUpdate(session, tableHandle, fragments); } } + + @Override + public Optional redirectTable(ConnectorSession session, SchemaTableName tableName) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.redirectTable(session, tableName); + } + } } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveConfig.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveConfig.java index 8654adc40dea..9eceb728edb0 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveConfig.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveConfig.java @@ -30,6 +30,7 @@ import javax.validation.constraints.AssertTrue; import javax.validation.constraints.Max; import javax.validation.constraints.Min; +import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.List; @@ -146,6 +147,9 @@ public class HiveConfig private boolean legacyHiveViewTranslation; + private boolean redirectToIcebergEnabled; + private String redirectToIcebergCatalog = "iceberg"; + public int getMaxInitialSplits() { return maxInitialSplits; @@ -1040,4 +1044,32 @@ public boolean isLegacyHiveViewTranslation() { return this.legacyHiveViewTranslation; } + + public boolean isRedirectToIcebergEnabled() + { + return redirectToIcebergEnabled; + } + + @Config("hive.redirect-to-iceberg-enabled") + @ConfigDescription("Redirect to a catalog configured with Iceberg Connector") + public HiveConfig setRedirectToIcebergEnabled(boolean redirectToIcebergEnabled) + { + this.redirectToIcebergEnabled = redirectToIcebergEnabled; + return this; + } + + @NotNull + @NotEmpty(message = "hive.redirect-to-iceberg-catalog cannot be empty") + public String getRedirectToIcebergCatalog() + { + return redirectToIcebergCatalog; + } + + @Config("hive.redirect-to-iceberg-catalog") + @ConfigDescription("The Iceberg catalog to redirect to") + public HiveConfig setRedirectToIcebergCatalog(String redirectToIcebergCatalog) + { + this.redirectToIcebergCatalog = redirectToIcebergCatalog; + return this; + } } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java index 61769f05e5ce..be8d1d8cd0cf 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveMetadata.java @@ -56,6 +56,7 @@ import io.trino.spi.block.Block; import io.trino.spi.connector.Assignment; 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.ConnectorInsertTableHandle; @@ -74,6 +75,8 @@ import io.trino.spi.connector.ConstraintApplicationResult; import io.trino.spi.connector.DiscretePredicates; import io.trino.spi.connector.InMemoryRecordSet; +import io.trino.spi.connector.ListTableColumnsResult; +import io.trino.spi.connector.ListTablePrivilegesResult; import io.trino.spi.connector.ProjectionApplicationResult; import io.trino.spi.connector.SchemaNotFoundException; import io.trino.spi.connector.SchemaTableName; @@ -86,7 +89,6 @@ import io.trino.spi.predicate.Domain; import io.trino.spi.predicate.NullableValue; import io.trino.spi.predicate.TupleDomain; -import io.trino.spi.security.GrantInfo; import io.trino.spi.security.Privilege; import io.trino.spi.security.RoleGrant; import io.trino.spi.security.TrinoPrincipal; @@ -122,6 +124,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; import java.util.Optional; import java.util.OptionalInt; import java.util.OptionalLong; @@ -130,6 +133,8 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -171,6 +176,7 @@ import static io.trino.plugin.hive.HivePartitionManager.extractPartitionValues; import static io.trino.plugin.hive.HiveSessionProperties.getCompressionCodec; import static io.trino.plugin.hive.HiveSessionProperties.getHiveStorageFormat; +import static io.trino.plugin.hive.HiveSessionProperties.getRedirectToIcebergCatalog; import static io.trino.plugin.hive.HiveSessionProperties.getTimestampPrecision; import static io.trino.plugin.hive.HiveSessionProperties.isBucketExecutionEnabled; import static io.trino.plugin.hive.HiveSessionProperties.isCollectColumnStatisticsOnWrite; @@ -178,6 +184,7 @@ import static io.trino.plugin.hive.HiveSessionProperties.isOptimizedMismatchedBucketCount; import static io.trino.plugin.hive.HiveSessionProperties.isParallelPartitionedBucketedInsert; import static io.trino.plugin.hive.HiveSessionProperties.isProjectionPushdownEnabled; +import static io.trino.plugin.hive.HiveSessionProperties.isRedirectToIcebergEnabled; import static io.trino.plugin.hive.HiveSessionProperties.isRespectTableFormat; import static io.trino.plugin.hive.HiveSessionProperties.isSortedWritingEnabled; import static io.trino.plugin.hive.HiveSessionProperties.isStatisticsEnabled; @@ -300,6 +307,8 @@ public class HiveMetadata public static final String AVRO_SCHEMA_URL_KEY = "avro.schema.url"; public static final String SPARK_TABLE_PROVIDER_KEY = "spark.sql.sources.provider"; public static final String DELTA_LAKE_PROVIDER = "delta"; + public static final String TABLE_TYPE_PROP = "table_type"; + public static final String ICEBERG_TABLE_TYPE_VALUE = "iceberg"; private static final String CSV_SEPARATOR_KEY = OpenCSVSerde.SEPARATORCHAR; private static final String CSV_QUOTE_KEY = OpenCSVSerde.QUOTECHAR; @@ -320,6 +329,12 @@ public class HiveMetadata private final HiveStatisticsProvider hiveStatisticsProvider; private final AccessControlMetadata accessControlMetadata; + // Copied from IcebergTableHandle + private static final Pattern ICEBERG_TABLE_NAME_PATTERN = Pattern.compile("" + + "(?[^$@]+)" + + "(?:@(?[0-9]+))?" + + "(?:\\$(?[^@]+)(?:@(?[0-9]+))?)?"); + public HiveMetadata( CatalogName catalogName, SemiTransactionalHiveMetastore metastore, @@ -369,6 +384,7 @@ public List listSchemaNames(ConnectorSession session) public HiveTableHandle getTableHandle(ConnectorSession session, SchemaTableName tableName) { requireNonNull(tableName, "tableName is null"); + rejectRedirectedTables(session, tableName); if (!filterSchema(tableName.getSchemaName())) { return null; } @@ -400,6 +416,7 @@ public HiveTableHandle getTableHandle(ConnectorSession session, SchemaTableName @Override public ConnectorTableHandle getTableHandleForStatisticsCollection(ConnectorSession session, SchemaTableName tableName, Map analyzeProperties) { + rejectRedirectedTables(session, tableName); HiveTableHandle handle = getTableHandle(session, tableName); if (handle == null) { return null; @@ -447,6 +464,9 @@ public ConnectorTableHandle getTableHandleForStatisticsCollection(ConnectorSessi @Override public Optional getSystemTable(ConnectorSession session, SchemaTableName tableName) { + if (redirectTable(session, tableName).isPresent()) { + return Optional.empty(); + } if (SystemTableHandler.PARTITIONS.matches(tableName)) { return getPartitionsSystemTable(session, tableName, SystemTableHandler.PARTITIONS.getSourceTableName(tableName)); } @@ -719,28 +739,32 @@ public Map getColumnHandles(ConnectorSession session, Conn @SuppressWarnings("TryWithIdenticalCatches") @Override - public Map> listTableColumns(ConnectorSession session, SchemaTablePrefix prefix) + public Stream listTableColumnsStream(ConnectorSession session, SchemaTablePrefix prefix) { requireNonNull(prefix, "prefix is null"); - ImmutableMap.Builder> columns = ImmutableMap.builder(); - for (SchemaTableName tableName : listTables(session, prefix)) { - try { - columns.put(tableName, getTableMetadata(session, tableName).getColumns()); - } - catch (HiveViewNotSupportedException e) { - // view is not supported - } - catch (TableNotFoundException e) { - // table disappeared during listing operation - } - catch (TrinoException e) { - // Skip this table if there's a failure due to Hive, a bad Serde, or bad metadata - if (!e.getErrorCode().getType().equals(ErrorType.EXTERNAL)) { - throw e; - } - } - } - return columns.build(); + return listTables(session, prefix).stream() + .map(tableName -> { + try { + if (redirectTable(session, tableName).isPresent()) { + return new ListTableColumnsResult(tableName, Optional.empty()); + } + return new ListTableColumnsResult(tableName, Optional.of(getTableMetadata(session, tableName).getColumns())); + } + catch (HiveViewNotSupportedException e) { + // view is not supported + } + catch (TableNotFoundException e) { + // table disappeared during listing operation + } + catch (TrinoException e) { + // Skip this table if there's a failure due to Hive, a bad Serde, or bad metadata + if (!e.getErrorCode().getType().equals(ErrorType.EXTERNAL)) { + throw e; + } + } + return null; + }) + .filter(Objects::nonNull); } @Override @@ -1178,6 +1202,7 @@ public void dropColumn(ConnectorSession session, ConnectorTableHandle tableHandl @Override public void setTableAuthorization(ConnectorSession session, SchemaTableName table, TrinoPrincipal principal) { + rejectRedirectedTables(session, table); metastore.setTableOwner(new HiveIdentity(session), table.getSchemaName(), table.getTableName(), HivePrincipal.from(principal)); } @@ -2699,19 +2724,39 @@ public Set listEnabledRoles(ConnectorSession session) @Override public void grantTablePrivileges(ConnectorSession session, SchemaTableName schemaTableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) { + rejectRedirectedTables(session, schemaTableName); accessControlMetadata.grantTablePrivileges(session, schemaTableName, privileges, HivePrincipal.from(grantee), grantOption); } @Override public void revokeTablePrivileges(ConnectorSession session, SchemaTableName schemaTableName, Set privileges, TrinoPrincipal grantee, boolean grantOption) { + rejectRedirectedTables(session, schemaTableName); accessControlMetadata.revokeTablePrivileges(session, schemaTableName, privileges, HivePrincipal.from(grantee), grantOption); } @Override - public List listTablePrivileges(ConnectorSession session, SchemaTablePrefix schemaTablePrefix) + public Stream listTablePrivilegesStream(ConnectorSession session, SchemaTablePrefix prefix) { - return accessControlMetadata.listTablePrivileges(session, listTables(session, schemaTablePrefix)); + if (isRedirectToIcebergEnabled(session)) { + ImmutableList.Builder redirectedTables = ImmutableList.builder(); + ImmutableList.Builder notRedirectedTables = ImmutableList.builder(); + listTables(session, prefix).forEach(table -> { + if (redirectTable(session, table).isPresent()) { + redirectedTables.add(table); + } + else { + notRedirectedTables.add(table); + } + }); + return Stream.concat( + accessControlMetadata.listTablePrivileges(session, notRedirectedTables.build()).stream() + .map(grantInfo -> new ListTablePrivilegesResult(grantInfo.getSchemaTableName(), Optional.of(grantInfo))), + redirectedTables.build().stream() + .map(table -> new ListTablePrivilegesResult(table, Optional.empty()))); + } + return accessControlMetadata.listTablePrivileges(session, listTables(session, prefix)).stream() + .map(grantInfo -> new ListTablePrivilegesResult(grantInfo.getSchemaTableName(), Optional.of(grantInfo))); } private static HiveStorageFormat extractHiveStorageFormat(Table table) @@ -2948,6 +2993,37 @@ public void cleanupQuery(ConnectorSession session) metastore.cleanupQuery(session); } + @Override + public Optional redirectTable(ConnectorSession session, SchemaTableName tableName) + { + if (isRedirectToIcebergEnabled(session) && filterSchema(tableName.getSchemaName()) && isExistingIcebergTable(session, tableName)) { + return Optional.of(new CatalogSchemaTableName(getRedirectToIcebergCatalog(session), tableName)); + } + return Optional.empty(); + } + + private boolean isExistingIcebergTable(ConnectorSession session, SchemaTableName schemaTableName) + { + String tableName = schemaTableName.getTableName(); + Matcher icebergNameMatch = ICEBERG_TABLE_NAME_PATTERN.matcher(tableName); + if (icebergNameMatch.matches()) { + tableName = icebergNameMatch.group("table"); + } + Optional
table = metastore.getTable(new HiveIdentity(session), schemaTableName.getSchemaName(), tableName); + if (table.isEmpty()) { + return false; + } + return ICEBERG_TABLE_TYPE_VALUE.equalsIgnoreCase(table.get().getParameters().get(TABLE_TYPE_PROP)); + } + + private void rejectRedirectedTables(ConnectorSession session, SchemaTableName tableName) + { + if (redirectTable(session, tableName).isPresent()) { + throw new TrinoException(NOT_SUPPORTED, "Hive Connector doesn't support modification operations (write, DDL, comment, statistics collection, set authorization) " + + "on Iceberg tables when redirection to Iceberg catalog is enabled"); + } + } + public static Optional getSourceTableNameFromSystemTable(SchemaTableName tableName) { return Stream.of(SystemTableHandler.values()) diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveSessionProperties.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveSessionProperties.java index ed9628f0a2c8..5f93443e56fa 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveSessionProperties.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/HiveSessionProperties.java @@ -96,7 +96,8 @@ public final class HiveSessionProperties private static final String DYNAMIC_FILTERING_PROBE_BLOCKING_TIMEOUT = "dynamic_filtering_probe_blocking_timeout"; private static final String OPTIMIZE_SYMLINK_LISTING = "optimize_symlink_listing"; private static final String LEGACY_HIVE_VIEW_TRANSLATION = "legacy_hive_view_translation"; - + private static final String REDIRECT_TO_ICEBERG_ENABLED = "redirect_to_iceberg_enabled"; + private static final String REDIRECT_TO_ICEBERG_CATALOG = "redirect_to_iceberg_catalog"; private final List> sessionProperties; public enum InsertExistingPartitionsBehavior @@ -402,6 +403,16 @@ public HiveSessionProperties( LEGACY_HIVE_VIEW_TRANSLATION, "Use legacy Hive view translation mechanism", hiveConfig.isLegacyHiveViewTranslation(), + false), + booleanProperty( + REDIRECT_TO_ICEBERG_ENABLED, + "Enable redirecting to a catalog configured with Iceberg Connector", + hiveConfig.isRedirectToIcebergEnabled(), + false), + stringProperty( + REDIRECT_TO_ICEBERG_CATALOG, + "The target Iceberg catalog for redirection", + hiveConfig.getRedirectToIcebergCatalog(), false)); } @@ -675,4 +686,14 @@ public static boolean isLegacyHiveViewTranslation(ConnectorSession session) { return session.getProperty(LEGACY_HIVE_VIEW_TRANSLATION, Boolean.class); } + + public static boolean isRedirectToIcebergEnabled(ConnectorSession session) + { + return session.getProperty(REDIRECT_TO_ICEBERG_ENABLED, Boolean.class); + } + + public static String getRedirectToIcebergCatalog(ConnectorSession session) + { + return session.getProperty(REDIRECT_TO_ICEBERG_CATALOG, String.class); + } } diff --git a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/AbstractTestHive.java b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/AbstractTestHive.java index 254e4262e3b6..0f5eb05ef1bd 100644 --- a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/AbstractTestHive.java +++ b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/AbstractTestHive.java @@ -89,6 +89,7 @@ import io.trino.spi.connector.ConstraintApplicationResult; import io.trino.spi.connector.DiscretePredicates; import io.trino.spi.connector.DynamicFilter; +import io.trino.spi.connector.ListTableColumnsResult; import io.trino.spi.connector.ProjectionApplicationResult; import io.trino.spi.connector.RecordCursor; import io.trino.spi.connector.RecordPageSource; @@ -999,7 +1000,9 @@ public void testGetAllTableColumns() { try (Transaction transaction = newTransaction()) { ConnectorMetadata metadata = transaction.getMetadata(); - Map> allColumns = metadata.listTableColumns(newSession(), new SchemaTablePrefix()); + Map> allColumns = metadata.listTableColumnsStream(newSession(), new SchemaTablePrefix()) + .filter(result -> result.getColumns().isPresent()) + .collect(toImmutableMap(ListTableColumnsResult::getTableName, result -> result.getColumns().get())); assertTrue(allColumns.containsKey(tablePartitionFormat)); assertTrue(allColumns.containsKey(tableUnpartitioned)); } @@ -1010,7 +1013,9 @@ public void testGetAllTableColumnsInSchema() { try (Transaction transaction = newTransaction()) { ConnectorMetadata metadata = transaction.getMetadata(); - Map> allColumns = metadata.listTableColumns(newSession(), new SchemaTablePrefix(database)); + Map> allColumns = metadata.listTableColumnsStream(newSession(), new SchemaTablePrefix(database)) + .filter(result -> result.getColumns().isPresent()) + .collect(toImmutableMap(ListTableColumnsResult::getTableName, result -> result.getColumns().get())); assertTrue(allColumns.containsKey(tablePartitionFormat)); assertTrue(allColumns.containsKey(tableUnpartitioned)); } @@ -1024,7 +1029,7 @@ public void testListUnknownSchema() ConnectorSession session = newSession(); assertNull(metadata.getTableHandle(session, new SchemaTableName(INVALID_DATABASE, INVALID_TABLE))); assertEquals(metadata.listTables(session, Optional.of(INVALID_DATABASE)), ImmutableList.of()); - assertEquals(metadata.listTableColumns(session, new SchemaTablePrefix(INVALID_DATABASE, INVALID_TABLE)), ImmutableMap.of()); + assertEquals(metadata.listTableColumnsStream(session, new SchemaTablePrefix(INVALID_DATABASE, INVALID_TABLE)).count(), 0L); assertEquals(metadata.listViews(session, Optional.of(INVALID_DATABASE)), ImmutableList.of()); assertEquals(metadata.getViews(session, Optional.of(INVALID_DATABASE)), ImmutableMap.of()); assertEquals(metadata.getView(session, new SchemaTableName(INVALID_DATABASE, INVALID_TABLE)), Optional.empty()); @@ -1273,9 +1278,11 @@ public void testGetTableSchemaOffline() { try (Transaction transaction = newTransaction()) { ConnectorMetadata metadata = transaction.getMetadata(); - Map> columns = metadata.listTableColumns(newSession(), tableOffline.toSchemaTablePrefix()); + List columns = metadata.listTableColumnsStream(newSession(), tableOffline.toSchemaTablePrefix()) + .filter(result -> result.getColumns().isPresent()) + .collect(toImmutableList()); assertEquals(columns.size(), 1); - Map map = uniqueIndex(getOnlyElement(columns.values()), ColumnMetadata::getName); + Map map = uniqueIndex(getOnlyElement(columns).getColumns().get(), ColumnMetadata::getName); assertPrimitiveField(map, "t_string", createUnboundedVarcharType(), false); } @@ -2186,7 +2193,7 @@ public void testHiveViewsHaveNoColumns() { try (Transaction transaction = newTransaction()) { ConnectorMetadata metadata = transaction.getMetadata(); - assertEquals(metadata.listTableColumns(newSession(), new SchemaTablePrefix(view.getSchemaName(), view.getTableName())), ImmutableMap.of()); + assertEquals(metadata.listTableColumnsStream(newSession(), new SchemaTablePrefix(view.getSchemaName(), view.getTableName())).count(), 0L); } } @@ -2713,16 +2720,16 @@ public void testHideDeltaLakeTables() .doesNotContain(tableName); // list all columns - assertThat(metadata.listTableColumns(session, new SchemaTablePrefix()).keySet()) - .doesNotContain(tableName); + assertEquals(metadata.listTableColumnsStream(session, new SchemaTablePrefix()) + .filter(result -> result.getTableName().equals(tableName)).count(), 0L); // list all columns in a schema - assertThat(metadata.listTableColumns(session, new SchemaTablePrefix(tableName.getSchemaName())).keySet()) - .doesNotContain(tableName); + assertEquals(metadata.listTableColumnsStream(session, new SchemaTablePrefix(tableName.getSchemaName())) + .filter(result -> result.getTableName().equals(tableName)).count(), 0L); // list all columns in a table - assertThat(metadata.listTableColumns(session, new SchemaTablePrefix(tableName.getSchemaName(), tableName.getTableName())).keySet()) - .doesNotContain(tableName); + assertEquals(metadata.listTableColumnsStream(session, new SchemaTablePrefix(tableName.getSchemaName(), tableName.getTableName())) + .filter(result -> result.getTableName().equals(tableName)).count(), 0L); } } finally { @@ -2895,8 +2902,10 @@ public void testIllegalStorageFormatDuringTableScan() // to make sure it can still be retrieved instead of throwing exception. try (Transaction transaction = newTransaction()) { ConnectorMetadata metadata = transaction.getMetadata(); - Map> allColumns = metadata.listTableColumns(newSession(), new SchemaTablePrefix(schemaTableName.getSchemaName())); - assertTrue(allColumns.containsKey(schemaTableName)); + assertEquals(metadata.listTableColumnsStream(newSession(), new SchemaTablePrefix(schemaTableName.getSchemaName())) + .filter(result -> result.getTableName().equals(schemaTableName)) + .filter(result -> result.getColumns().isPresent()) + .count(), 1L); } finally { dropTable(schemaTableName); diff --git a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/TestHiveConfig.java b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/TestHiveConfig.java index 42ce9b024cbb..4c364567eada 100644 --- a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/TestHiveConfig.java +++ b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/TestHiveConfig.java @@ -101,7 +101,9 @@ public void testDefaults() .setDynamicFilteringProbeBlockingTimeout(new Duration(0, TimeUnit.MINUTES)) .setTimestampPrecision(HiveTimestampPrecision.DEFAULT_PRECISION) .setOptimizeSymlinkListing(true) - .setLegacyHiveViewTranslation(false)); + .setLegacyHiveViewTranslation(false) + .setRedirectToIcebergEnabled(false) + .setRedirectToIcebergCatalog("iceberg")); } @Test @@ -174,6 +176,8 @@ public void testExplicitPropertyMappings() .put("hive.timestamp-precision", "NANOSECONDS") .put("hive.optimize-symlink-listing", "false") .put("hive.legacy-hive-view-translation", "true") + .put("hive.redirect-to-iceberg-enabled", "true") + .put("hive.redirect-to-iceberg-catalog", "myiceberg") .build(); HiveConfig expected = new HiveConfig() @@ -242,7 +246,9 @@ public void testExplicitPropertyMappings() .setDynamicFilteringProbeBlockingTimeout(new Duration(10, TimeUnit.SECONDS)) .setTimestampPrecision(HiveTimestampPrecision.NANOSECONDS) .setOptimizeSymlinkListing(false) - .setLegacyHiveViewTranslation(true); + .setLegacyHiveViewTranslation(true) + .setRedirectToIcebergEnabled(true) + .setRedirectToIcebergCatalog("myiceberg"); assertFullMapping(properties, expected); } diff --git a/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/env/environment/SinglenodeHiveRedirectionToIceberg.java b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/env/environment/SinglenodeHiveRedirectionToIceberg.java new file mode 100644 index 000000000000..f4426214ec74 --- /dev/null +++ b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/env/environment/SinglenodeHiveRedirectionToIceberg.java @@ -0,0 +1,51 @@ +/* + * 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.tests.product.launcher.env.environment; + +import com.google.common.collect.ImmutableList; +import io.trino.tests.product.launcher.docker.DockerFiles; +import io.trino.tests.product.launcher.env.Environment; +import io.trino.tests.product.launcher.env.EnvironmentProvider; +import io.trino.tests.product.launcher.env.common.Hadoop; +import io.trino.tests.product.launcher.env.common.Standard; +import io.trino.tests.product.launcher.env.common.TestsEnvironment; + +import javax.inject.Inject; + +import static io.trino.tests.product.launcher.env.common.Hadoop.CONTAINER_PRESTO_HIVE_PROPERTIES; +import static io.trino.tests.product.launcher.env.common.Hadoop.CONTAINER_PRESTO_ICEBERG_PROPERTIES; +import static java.util.Objects.requireNonNull; +import static org.testcontainers.utility.MountableFile.forHostPath; + +@TestsEnvironment +public class SinglenodeHiveRedirectionToIceberg + extends EnvironmentProvider +{ + private final DockerFiles dockerFiles; + + @Inject + public SinglenodeHiveRedirectionToIceberg(DockerFiles dockerFiles, Standard standard, Hadoop hadoop) + { + super(ImmutableList.of(standard, hadoop)); + this.dockerFiles = requireNonNull(dockerFiles, "dockerFiles is null"); + } + + @Override + public void extendEnvironment(Environment.Builder builder) + { + builder.configureContainer("presto-master", container -> container + .withCopyFileToContainer(forHostPath(dockerFiles.getDockerFilesHostPath("conf/environment/singlenode-hive-redirection-to-iceberg/hive.properties")), CONTAINER_PRESTO_HIVE_PROPERTIES) + .withCopyFileToContainer(forHostPath(dockerFiles.getDockerFilesHostPath("conf/environment/singlenode-hive-redirection-to-iceberg/iceberg.properties")), CONTAINER_PRESTO_ICEBERG_PROPERTIES)); + } +} diff --git a/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java index 23513cea51dd..20d1127dc664 100644 --- a/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java +++ b/testing/trino-product-tests-launcher/src/main/java/io/trino/tests/product/launcher/suite/suites/Suite7NonGeneric.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableList; import io.trino.tests.product.launcher.env.EnvironmentConfig; import io.trino.tests.product.launcher.env.EnvironmentDefaults; +import io.trino.tests.product.launcher.env.environment.SinglenodeHiveRedirectionToIceberg; import io.trino.tests.product.launcher.env.environment.SinglenodeKerberosHdfsImpersonationCrossRealm; import io.trino.tests.product.launcher.env.environment.SinglenodeLdapBindDn; import io.trino.tests.product.launcher.env.environment.SinglenodeMysql; @@ -45,6 +46,7 @@ public List getTestRuns(EnvironmentConfig config) testOnEnvironment(SinglenodePostgresql.class).withGroups("postgresql").build(), testOnEnvironment(SinglenodeSqlserver.class).withGroups("sqlserver").build(), testOnEnvironment(SinglenodeSparkIceberg.class).withGroups("iceberg").withExcludedGroups("storage_formats").build(), + testOnEnvironment(SinglenodeHiveRedirectionToIceberg.class).withGroups("hive_redirection_to_iceberg").build(), testOnEnvironment(SinglenodeKerberosHdfsImpersonationCrossRealm.class).withGroups("storage_formats", "cli", "hdfs_impersonation").build(), testOnEnvironment(TwoMixedHives.class).withGroups("two_hives").build(), testOnEnvironment(TwoKerberosHives.class).withGroups("two_hives").build(), diff --git a/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-hive-redirection-to-iceberg/hive.properties b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-hive-redirection-to-iceberg/hive.properties new file mode 100644 index 000000000000..1e28f7f095cc --- /dev/null +++ b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-hive-redirection-to-iceberg/hive.properties @@ -0,0 +1,11 @@ +connector.name=hive-hadoop2 +hive.metastore.uri=thrift://hadoop-master:9083 +hive.config.resources=/docker/presto-product-tests/conf/presto/etc/hive-default-fs-site.xml +hive.allow-add-column=true +hive.allow-drop-column=true +hive.allow-rename-column=true +hive.allow-comment-table=true +hive.allow-drop-table=true +hive.allow-rename-table=true +hive.redirect-to-iceberg-enabled=true +hive.redirect-to-iceberg-catalog=iceberg diff --git a/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-hive-redirection-to-iceberg/iceberg.properties b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-hive-redirection-to-iceberg/iceberg.properties new file mode 100644 index 000000000000..6230d550a4ac --- /dev/null +++ b/testing/trino-product-tests-launcher/src/main/resources/docker/presto-product-tests/conf/environment/singlenode-hive-redirection-to-iceberg/iceberg.properties @@ -0,0 +1,3 @@ +connector.name=iceberg +hive.metastore.uri=thrift://hadoop-master:9083 +hive.config.resources=/docker/presto-product-tests/conf/presto/etc/hive-default-fs-site.xml diff --git a/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java b/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java index ea8584ba2276..24eb9517e480 100644 --- a/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java +++ b/testing/trino-product-tests/src/main/java/io/trino/tests/TestGroups.java @@ -43,6 +43,7 @@ public final class TestGroups public static final String HIVE_VIEWS = "hive_views"; public static final String HIVE_CACHING = "hive_caching"; public static final String HIVE_WITH_EXTERNAL_WRITES = "hive_with_external_writes"; + public static final String HIVE_REDIRECTION_TO_ICEBERG = "hive_redirection_to_iceberg"; public static final String AUTHORIZATION = "authorization"; public static final String HIVE_COERCION = "hive_coercion"; public static final String CASSANDRA = "cassandra"; diff --git a/testing/trino-product-tests/src/main/java/io/trino/tests/hive/TestHiveRedirectionToIceberg.java b/testing/trino-product-tests/src/main/java/io/trino/tests/hive/TestHiveRedirectionToIceberg.java new file mode 100644 index 000000000000..d321e0806ae5 --- /dev/null +++ b/testing/trino-product-tests/src/main/java/io/trino/tests/hive/TestHiveRedirectionToIceberg.java @@ -0,0 +1,178 @@ +/* + * 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.tests.hive; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.assertj.core.api.Condition; +import org.testng.annotations.Test; + +import java.sql.Date; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static io.trino.tempto.assertions.QueryAssert.Row; +import static io.trino.tempto.assertions.QueryAssert.Row.row; +import static io.trino.tempto.assertions.QueryAssert.assertThat; +import static io.trino.tests.TestGroups.HIVE_REDIRECTION_TO_ICEBERG; +import static io.trino.tests.TestGroups.PROFILE_SPECIFIC_TESTS; +import static io.trino.tests.utils.QueryExecutors.onPresto; +import static java.lang.String.format; +import static org.testng.Assert.assertNull; + +public class TestHiveRedirectionToIceberg + extends HiveProductTest +{ + private static final String CREATE_ICEBERG_TABLE_TEMPLATE = "CREATE TABLE %s (_string VARCHAR, _bigint BIGINT, _date DATE) WITH (partitioning = ARRAY['_bigint', '_date'])"; + private static final String CREATE_HIVE_TABLE_TEMPLATE = "CREATE TABLE %s (_string VARCHAR, _bigint BIGINT, _date DATE) WITH (partitioned_by = ARRAY['_bigint', '_date'])"; + private static final String INSERT_TEMPLATE = "INSERT INTO %s VALUES " + + "(NULL, NULL, NULL), " + + "('abc', 1, DATE '2020-08-04'), " + + "('abcdefghijklmnopqrstuvwxyz', 2, DATE '2020-08-04')"; + private static final List EXPECTED_ROWS = ImmutableList.builder() + .add(row(null, null, null)) + .add(row("abc", 1, Date.valueOf("2020-08-04"))) + .add(row("abcdefghijklmnopqrstuvwxyz", 2, Date.valueOf("2020-08-04"))) + .build(); + private static final Pattern TABLE_COMMENT_EXTRACTER = Pattern.compile(".*?COMMENT\\s*'(.*?)'.*$", Pattern.DOTALL); + private static final String NOT_SUPPORTED_ERROR_MESSAGE = "Hive Connector doesn't support modification operations (write, DDL, comment, statistics collection, set authorization) " + + "on Iceberg tables when redirection to Iceberg catalog is enabled"; + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testSelectSuccess() + { + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.default.test_select_drop")); + onPresto().executeQuery(format(INSERT_TEMPLATE, "iceberg.default.test_select_drop")); + + assertThat(onPresto().executeQuery("SELECT * FROM iceberg.default.test_select_drop")).containsOnly(EXPECTED_ROWS); + assertThat(onPresto().executeQuery("SELECT * FROM hive.default.test_select_drop")).containsOnly(EXPECTED_ROWS); + assertThat(onPresto().executeQuery("SELECT _bigint, _date FROM hive.default.\"test_select_drop$partitions\"")).containsOnly( + row(null, null), + row(1, Date.valueOf("2020-08-04")), + row(2, Date.valueOf("2020-08-04"))); + + onPresto().executeQuery("DROP TABLE iceberg.default.test_select_drop"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testInsertFailure() + { + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.default.test_insert")); + + assertThat(() -> onPresto().executeQuery(format(INSERT_TEMPLATE, "hive.default.test_insert"))) + .failsWithMessage(NOT_SUPPORTED_ERROR_MESSAGE); + + onPresto().executeQuery("DROP TABLE iceberg.default.test_insert"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testDescribeSuccess() + { + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.default.test_describe")); + + assertThat(onPresto().executeQuery("DESCRIBE hive.default.test_describe")) + .satisfies(new Condition<>(queryResult -> { + Set actualColumns = ImmutableSet.copyOf(queryResult.column(1)); + Set expectedColumns = ImmutableSet.of("_string", "_bigint", "_date"); + return actualColumns.equals(expectedColumns); + }, "equals")); + + onPresto().executeQuery("DROP TABLE iceberg.default.test_describe"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testShowCreateTableSuccess() + { + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.default.test_show_create")); + + assertThat(onPresto().executeQuery("SHOW CREATE TABLE hive.default.test_show_create")).hasRowsCount(1); + + onPresto().executeQuery("DROP TABLE iceberg.default.test_show_create"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testAlterTableFailure() + { + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.default.test_alter_table")); + onPresto().executeQuery(format(INSERT_TEMPLATE, "iceberg.default.test_alter_table")); + + assertThat(() -> onPresto().executeQuery("ALTER TABLE hive.default.test_alter_table RENAME TO default.test_alter_table_new")) + .failsWithMessage(NOT_SUPPORTED_ERROR_MESSAGE); + + assertThat(() -> onPresto().executeQuery("ALTER TABLE hive.default.test_alter_table ADD COLUMN _double DOUBLE")) + .failsWithMessage(NOT_SUPPORTED_ERROR_MESSAGE); + assertThat(() -> onPresto().executeQuery("ALTER TABLE hive.default.test_alter_table DROP COLUMN _string")) + .failsWithMessage(NOT_SUPPORTED_ERROR_MESSAGE); + assertThat(() -> onPresto().executeQuery("ALTER TABLE hive.default.test_alter_table RENAME COLUMN _bigint TO _bi")) + .failsWithMessage(NOT_SUPPORTED_ERROR_MESSAGE); + + onPresto().executeQuery("DROP TABLE iceberg.default.test_alter_table"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testCommentTableFailure() + { + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.default.test_comment_table")); + + assertNull(extractTableComment((String) onPresto().executeQuery("SHOW CREATE TABLE hive.default.test_comment_table").row(0).get(0))); + + assertThat(() -> onPresto().executeQuery("COMMENT ON TABLE hive.default.test_comment_table IS 'this is an iceberg table'")) + .failsWithMessage(NOT_SUPPORTED_ERROR_MESSAGE); + + onPresto().executeQuery("DROP TABLE iceberg.default.test_comment_table"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testCreateHiveTableSuccess() + { + onPresto().executeQuery(format(CREATE_HIVE_TABLE_TEMPLATE, "hive.default.test_create_hive_table")); + onPresto().executeQuery(format(INSERT_TEMPLATE, "hive.default.test_create_hive_table")); + + assertThat(onPresto().executeQuery("SELECT * FROM hive.default.test_create_hive_table")).containsOnly(EXPECTED_ROWS); + assertThat(() -> onPresto().executeQuery("SELECT * FROM iceberg.default.test_create_hive_table")) + .failsWithMessage("Not an Iceberg table: default.test_create_hive_table"); + + onPresto().executeQuery("DROP TABLE hive.default.test_create_hive_table"); + } + + @Test(groups = {HIVE_REDIRECTION_TO_ICEBERG, PROFILE_SPECIFIC_TESTS}) + public void testInformationSchemaColumnsSuccess() + { + onPresto().executeQuery("CREATE SCHEMA iceberg.redirection_schema"); + onPresto().executeQuery(format(CREATE_ICEBERG_TABLE_TEMPLATE, "iceberg.redirection_schema.test_info_schema_table")); + + List expectedColumns = ImmutableList.builder() + .add(row("hive", "redirection_schema", "test_info_schema_table", "_string", 1, null, "YES", "varchar")) + .add(row("hive", "redirection_schema", "test_info_schema_table", "_bigint", 2, null, "YES", "bigint")) + .add(row("hive", "redirection_schema", "test_info_schema_table", "_date", 3, null, "YES", "date")) + .build(); + assertThat(onPresto().executeQuery("SELECT * FROM hive.information_schema.columns")).contains(expectedColumns); + assertThat(onPresto().executeQuery("SELECT * FROM hive.information_schema.columns WHERE table_schema = 'redirection_schema'")).containsOnly(expectedColumns); + assertThat(onPresto().executeQuery("SELECT * FROM hive.information_schema.columns WHERE table_schema = 'redirection_schema' AND table_name = 'test_info_schema_table'")).containsOnly(expectedColumns); + + onPresto().executeQuery("DROP TABLE iceberg.redirection_schema.test_info_schema_table"); + onPresto().executeQuery("DROP SCHEMA iceberg.redirection_schema"); + } + + private static String extractTableComment(String sql) + { + Matcher matcher = TABLE_COMMENT_EXTRACTER.matcher(sql); + if (matcher.matches()) { + return matcher.group(1); + } + return null; + } +}