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 63da5ff06730..3bbfd2df1d91 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 @@ -196,9 +196,10 @@ else if (element instanceof LikeClause) { TableHandle likeTable = redirection.getTableHandle() .orElseThrow(() -> semanticException(TABLE_NOT_FOUND, statement, "LIKE table '%s' does not exist", originalLikeTableName)); + LikeClause.PropertiesOption propertiesOption = likeClause.getPropertiesOption().orElse(EXCLUDING); QualifiedObjectName likeTableName = redirection.getRedirectedTableName().orElse(originalLikeTableName); - if (!tableName.getCatalogName().equals(likeTableName.getCatalogName())) { - String message = "CREATE TABLE LIKE across catalogs is not supported"; + if (propertiesOption == INCLUDING && !tableName.getCatalogName().equals(likeTableName.getCatalogName())) { + String message = "CREATE TABLE LIKE table INCLUDING PROPERTIES across catalogs is not supported"; if (!originalLikeTableName.equals(likeTableName)) { message += format(". LIKE table '%s' redirected to '%s'.", originalLikeTableName, likeTableName); } @@ -207,8 +208,7 @@ else if (element instanceof LikeClause) { TableMetadata likeTableMetadata = plannerContext.getMetadata().getTableMetadata(session, likeTable); - Optional propertiesOption = likeClause.getPropertiesOption(); - if (propertiesOption.isPresent() && propertiesOption.get() == LikeClause.PropertiesOption.INCLUDING) { + if (propertiesOption == INCLUDING) { if (includingProperties) { throw semanticException(NOT_SUPPORTED, statement, "Only one LIKE clause can specify INCLUDING PROPERTIES"); } @@ -227,7 +227,7 @@ else if (element instanceof LikeClause) { catch (AccessDeniedException e) { throw new AccessDeniedException("Cannot reference columns of table " + likeTableName); } - if (propertiesOption.orElse(EXCLUDING) == INCLUDING) { + if (propertiesOption == INCLUDING) { try { accessControl.checkCanShowCreateTable(session.toSecurityContext(), likeTableName); } 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 d73d2e41fb7c..9abbdc586d8c 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 @@ -95,6 +95,7 @@ public class TestCreateTableTask { private static final String CATALOG_NAME = "catalog"; + private static final String OTHER_CATALOG_NAME = "other_catalog"; private static final ConnectorTableMetadata PARENT_TABLE = new ConnectorTableMetadata( new SchemaTableName("schema", "parent_table"), List.of(new ColumnMetadata("a", SMALLINT), new ColumnMetadata("b", BIGINT)), @@ -119,13 +120,21 @@ public void setUp() .withTableProperties(() -> ImmutableList.of(stringProperty("baz", "test property", null, false))) .build(), ImmutableMap.of()); + queryRunner.createCatalog( + OTHER_CATALOG_NAME, + MockConnectorFactory.builder().withName("other_mock").build(), + ImmutableMap.of()); tablePropertyManager = queryRunner.getTablePropertyManager(); columnPropertyManager = queryRunner.getColumnPropertyManager(); testSession = testSessionBuilder() .setTransactionId(transactionManager.beginTransaction(false)) .build(); - metadata = new MockMetadata(new CatalogName(CATALOG_NAME), emptySet()); + metadata = new MockMetadata( + Map.of( + CATALOG_NAME, new CatalogName(CATALOG_NAME), + OTHER_CATALOG_NAME, new CatalogName(OTHER_CATALOG_NAME)), + emptySet()); plannerContext = plannerContextBuilder().withMetadata(metadata).build(); } @@ -250,7 +259,7 @@ public void testCreateLike() } @Test - public void testCreateLikeWithProperties() + public void testCreateLikeIncludingProperties() { CreateTable statement = getCreateLikeStatement(true); @@ -264,6 +273,30 @@ public void testCreateLikeWithProperties() .isEqualTo(PARENT_TABLE.getProperties()); } + @Test + public void testCreateLikeExcludingPropertiesAcrossCatalogs() + { + CreateTable statement = getCreateLikeStatement(QualifiedName.of(OTHER_CATALOG_NAME, "other_schema", "test_table"), false); + + CreateTableTask createTableTask = new CreateTableTask(plannerContext, new AllowAllAccessControl(), columnPropertyManager, tablePropertyManager); + getFutureValue(createTableTask.internalExecute(statement, testSession, List.of(), output -> {})); + assertEquals(metadata.getCreateTableCallCount(), 1); + + assertThat(metadata.getReceivedTableMetadata().get(0).getColumns()) + .isEqualTo(PARENT_TABLE.getColumns()); + } + + @Test + public void testCreateLikeIncludingPropertiesAcrossCatalogs() + { + CreateTable failingStatement = getCreateLikeStatement(QualifiedName.of(OTHER_CATALOG_NAME, "other_schema", "test_table"), true); + + CreateTableTask failingCreateTableTask = new CreateTableTask(plannerContext, new AllowAllAccessControl(), columnPropertyManager, tablePropertyManager); + assertThatThrownBy(() -> getFutureValue(failingCreateTableTask.internalExecute(failingStatement, testSession, List.of(), output -> {}))) + .isInstanceOf(TrinoException.class) + .hasMessageContaining("CREATE TABLE LIKE table INCLUDING PROPERTIES across catalogs is not supported"); + } + @Test public void testCreateLikeDenyPermission() { @@ -279,7 +312,7 @@ public void testCreateLikeDenyPermission() } @Test - public void testCreateLikeWithPropertiesDenyPermission() + public void testCreateLikeIncludingPropertiesDenyPermission() { CreateTable statement = getCreateLikeStatement(true); @@ -293,9 +326,14 @@ public void testCreateLikeWithPropertiesDenyPermission() } private CreateTable getCreateLikeStatement(boolean includingProperties) + { + return getCreateLikeStatement(QualifiedName.of("test_table"), includingProperties); + } + + private CreateTable getCreateLikeStatement(QualifiedName name, boolean includingProperties) { return new CreateTable( - QualifiedName.of("test_table"), + name, List.of(new LikeClause(QualifiedName.of(PARENT_TABLE.getTable().getTableName()), includingProperties ? Optional.of(INCLUDING) : Optional.empty())), true, ImmutableList.of(), @@ -305,13 +343,13 @@ private CreateTable getCreateLikeStatement(boolean includingProperties) private static class MockMetadata extends AbstractMockMetadata { - private final CatalogName catalogHandle; + private final Map catalogHandles; private final List tables = new CopyOnWriteArrayList<>(); private Set connectorCapabilities; - public MockMetadata(CatalogName catalogHandle, Set connectorCapabilities) + public MockMetadata(Map catalogHandles, Set connectorCapabilities) { - this.catalogHandle = requireNonNull(catalogHandle, "catalogHandle is null"); + this.catalogHandles = requireNonNull(catalogHandles, "catalogHandles is null"); this.connectorCapabilities = immutableEnumSet(requireNonNull(connectorCapabilities, "connectorCapabilities is null")); } @@ -327,10 +365,7 @@ public void createTable(Session session, String catalogName, ConnectorTableMetad @Override public Optional getCatalogHandle(Session session, String catalogName) { - if (catalogHandle.getCatalogName().equals(catalogName)) { - return Optional.of(catalogHandle); - } - return Optional.empty(); + return Optional.ofNullable(catalogHandles.get(catalogName)); } @Override