diff --git a/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java b/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java index 66d5813a5da6..337b3090ee04 100644 --- a/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java +++ b/core/trino-main/src/test/java/io/trino/execution/BaseDataDefinitionTaskTest.java @@ -351,6 +351,19 @@ public void dropColumn(Session session, TableHandle tableHandle, ColumnHandle co tables.put(tableName, new ConnectorTableMetadata(tableName, columns)); } + @Override + public void renameColumn(Session session, TableHandle tableHandle, ColumnHandle source, String target) + { + SchemaTableName tableName = getTableName(tableHandle); + ConnectorTableMetadata metadata = tables.get(tableName); + String columnName = ((TestingColumnHandle) source).getName(); + + List columns = metadata.getColumns().stream() + .map(column -> column.getName().equals(columnName) ? ColumnMetadata.builderFrom(column).setName(target).build() : column) + .collect(toImmutableList()); + tables.put(tableName, new ConnectorTableMetadata(tableName, columns)); + } + @Override public void setColumnType(Session session, TableHandle tableHandle, ColumnHandle columnHandle, Type type) { diff --git a/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java b/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java new file mode 100644 index 000000000000..6a485e257fdc --- /dev/null +++ b/core/trino-main/src/test/java/io/trino/execution/TestRenameColumnTask.java @@ -0,0 +1,131 @@ +/* + * 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.execution; + +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ListenableFuture; +import io.trino.execution.warnings.WarningCollector; +import io.trino.metadata.QualifiedObjectName; +import io.trino.metadata.TableHandle; +import io.trino.security.AllowAllAccessControl; +import io.trino.spi.connector.ColumnMetadata; +import io.trino.spi.connector.ConnectorTableMetadata; +import io.trino.sql.tree.Identifier; +import io.trino.sql.tree.NodeLocation; +import io.trino.sql.tree.QualifiedName; +import io.trino.sql.tree.RenameColumn; +import org.testng.annotations.Test; + +import static io.airlift.concurrent.MoreFutures.getFutureValue; +import static io.trino.spi.StandardErrorCode.COLUMN_NOT_FOUND; +import static io.trino.spi.StandardErrorCode.TABLE_NOT_FOUND; +import static io.trino.spi.type.BigintType.BIGINT; +import static io.trino.sql.QueryUtil.identifier; +import static io.trino.testing.TestingHandles.TEST_CATALOG_NAME; +import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy; +import static org.assertj.core.api.Assertions.assertThat; + +@Test(singleThreaded = true) +public class TestRenameColumnTask + extends BaseDataDefinitionTaskTest +{ + @Test + public void testRenameColumn() + { + QualifiedObjectName tableName = qualifiedObjectName("existing_table"); + metadata.createTable(testSession, TEST_CATALOG_NAME, simpleTable(tableName), false); + TableHandle table = metadata.getTableHandle(testSession, tableName).get(); + assertThat(metadata.getTableMetadata(testSession, table).getColumns()) + .containsExactly(new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT)); + + getFutureValue(executeRenameColumn(asQualifiedName(tableName), identifier("a"), identifier("a_renamed"), false, false)); + assertThat(metadata.getTableMetadata(testSession, table).getColumns()) + .containsExactly(new ColumnMetadata("a_renamed", BIGINT), new ColumnMetadata("b", BIGINT)); + } + + @Test + public void testRenameColumnNotExistingTable() + { + QualifiedObjectName tableName = qualifiedObjectName("not_existing_table"); + + assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameColumn(asQualifiedName(tableName), identifier("a"), identifier("a_renamed"), false, false))) + .hasErrorCode(TABLE_NOT_FOUND) + .hasMessageContaining("Table '%s' does not exist", tableName); + } + + @Test + public void testRenameColumnNotExistingTableIfExists() + { + QualifiedName tableName = qualifiedName("not_existing_table"); + + getFutureValue(executeRenameColumn(tableName, identifier("a"), identifier("a_renamed"), true, false)); + // no exception + } + + @Test + public void testRenameMissingColumn() + { + QualifiedObjectName tableName = qualifiedObjectName("existing_table"); + metadata.createTable(testSession, TEST_CATALOG_NAME, simpleTable(tableName), false); + + assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameColumn(asQualifiedName(tableName), identifier("missing_column"), identifier("test"), false, false))) + .hasErrorCode(COLUMN_NOT_FOUND) + .hasMessageContaining("Column 'missing_column' does not exist"); + } + + @Test + public void testRenameColumnIfExists() + { + QualifiedObjectName tableName = qualifiedObjectName("existing_table"); + metadata.createTable(testSession, TEST_CATALOG_NAME, simpleTable(tableName), false); + TableHandle table = metadata.getTableHandle(testSession, tableName).get(); + + getFutureValue(executeRenameColumn(asQualifiedName(tableName), identifier("missing_column"), identifier("test"), false, true)); + assertThat(metadata.getTableMetadata(testSession, table).getColumns()) + .containsExactly(new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT)); + } + + @Test + public void testRenameColumnOnView() + { + QualifiedObjectName viewName = qualifiedObjectName("existing_view"); + metadata.createView(testSession, viewName, someView(), false); + + assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameColumn(asQualifiedName(viewName), identifier("a"), identifier("a_renamed"), false, false))) + .hasErrorCode(TABLE_NOT_FOUND) + .hasMessageContaining("Table '%s' does not exist", viewName); + } + + @Test + public void testRenameColumnOnMaterializedView() + { + QualifiedObjectName materializedViewName = qualifiedObjectName("existing_materialized_view"); + metadata.createMaterializedView(testSession, QualifiedObjectName.valueOf(materializedViewName.toString()), someMaterializedView(), false, false); + + assertTrinoExceptionThrownBy(() -> getFutureValue(executeRenameColumn(asQualifiedName(materializedViewName), identifier("a"), identifier("a_renamed"), false, false))) + .hasErrorCode(TABLE_NOT_FOUND) + .hasMessageContaining("Table '%s' does not exist", materializedViewName); + } + + private ListenableFuture executeRenameColumn(QualifiedName table, Identifier source, Identifier target, boolean tableExists, boolean columnExists) + { + return new RenameColumnTask(plannerContext.getMetadata(), new AllowAllAccessControl()) + .execute(new RenameColumn(new NodeLocation(1, 1), table, source, target, tableExists, columnExists), queryStateMachine, ImmutableList.of(), WarningCollector.NOOP); + } + + private static ConnectorTableMetadata simpleTable(QualifiedObjectName tableName) + { + return new ConnectorTableMetadata(tableName.asSchemaTableName(), ImmutableList.of(new ColumnMetadata("a", BIGINT), new ColumnMetadata("b", BIGINT))); + } +} diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/RenameColumn.java b/core/trino-parser/src/main/java/io/trino/sql/tree/RenameColumn.java index 02f2baf3dfbb..2edbc4fdac78 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/RenameColumn.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/RenameColumn.java @@ -31,19 +31,9 @@ public class RenameColumn private final boolean tableExists; private final boolean columnExists; - public RenameColumn(QualifiedName table, Identifier source, Identifier target, boolean tableExists, boolean columnExists) - { - this(Optional.empty(), table, source, target, tableExists, columnExists); - } - public RenameColumn(NodeLocation location, QualifiedName table, Identifier source, Identifier target, boolean tableExists, boolean columnExists) { - this(Optional.of(location), table, source, target, tableExists, columnExists); - } - - private RenameColumn(Optional location, QualifiedName table, Identifier source, Identifier target, boolean tableExists, boolean columnExists) - { - super(location); + super(Optional.of(location)); this.table = requireNonNull(table, "table is null"); this.source = requireNonNull(source, "source is null"); this.target = requireNonNull(target, "target is null"); diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java index 9cee3bf11232..0a1224fa5b59 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java @@ -2076,10 +2076,49 @@ public void testCommentColumn() @Test public void testRenameColumn() { - assertStatement("ALTER TABLE foo.t RENAME COLUMN a TO b", new RenameColumn(QualifiedName.of("foo", "t"), identifier("a"), identifier("b"), false, false)); - assertStatement("ALTER TABLE IF EXISTS foo.t RENAME COLUMN a TO b", new RenameColumn(QualifiedName.of("foo", "t"), identifier("a"), identifier("b"), true, false)); - assertStatement("ALTER TABLE foo.t RENAME COLUMN IF EXISTS a TO b", new RenameColumn(QualifiedName.of("foo", "t"), identifier("a"), identifier("b"), false, true)); - assertStatement("ALTER TABLE IF EXISTS foo.t RENAME COLUMN IF EXISTS a TO b", new RenameColumn(QualifiedName.of("foo", "t"), identifier("a"), identifier("b"), true, true)); + assertThat(statement("ALTER TABLE foo.t RENAME COLUMN a TO b")) + .isEqualTo(new RenameColumn( + location(1, 1), + QualifiedName.of(ImmutableList.of( + new Identifier(location(1, 13), "foo", false), + new Identifier(location(1, 17), "t", false))), + new Identifier(location(1, 33), "a", false), + new Identifier(location(1, 38), "b", false), + false, + false)); + + assertThat(statement("ALTER TABLE IF EXISTS foo.t RENAME COLUMN a TO b")) + .isEqualTo(new RenameColumn( + location(1, 1), + QualifiedName.of(ImmutableList.of( + new Identifier(location(1, 23), "foo", false), + new Identifier(location(1, 27), "t", false))), + new Identifier(location(1, 43), "a", false), + new Identifier(location(1, 48), "b", false), + true, + false)); + + assertThat(statement("ALTER TABLE foo.t RENAME COLUMN IF EXISTS a TO b")) + .isEqualTo(new RenameColumn( + location(1, 1), + QualifiedName.of(ImmutableList.of( + new Identifier(location(1, 13), "foo", false), + new Identifier(location(1, 17), "t", false))), + new Identifier(location(1, 43), "a", false), + new Identifier(location(1, 48), "b", false), + false, + true)); + + assertThat(statement("ALTER TABLE IF EXISTS foo.t RENAME COLUMN IF EXISTS a TO b")) + .isEqualTo(new RenameColumn( + location(1, 1), + QualifiedName.of(ImmutableList.of( + new Identifier(location(1, 23), "foo", false), + new Identifier(location(1, 27), "t", false))), + new Identifier(location(1, 53), "a", false), + new Identifier(location(1, 58), "b", false), + true, + true)); } @Test