Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support renaming a field with RENAME COLUMN in engine and Iceberg #16757

Merged
merged 2 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@
import io.trino.metadata.RedirectionAwareTableHandle;
import io.trino.metadata.TableHandle;
import io.trino.security.AccessControl;
import io.trino.spi.TrinoException;
import io.trino.spi.connector.ColumnHandle;
import io.trino.spi.connector.ColumnMetadata;
import io.trino.spi.type.RowType;
import io.trino.spi.type.Type;
import io.trino.sql.tree.Expression;
import io.trino.sql.tree.RenameColumn;

import java.util.List;
import java.util.Map;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Iterables.getLast;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
import static io.trino.metadata.MetadataUtil.createQualifiedObjectName;
import static io.trino.spi.StandardErrorCode.AMBIGUOUS_NAME;
import static io.trino.spi.StandardErrorCode.COLUMN_ALREADY_EXISTS;
import static io.trino.spi.StandardErrorCode.COLUMN_NOT_FOUND;
import static io.trino.spi.StandardErrorCode.NOT_SUPPORTED;
Expand Down Expand Up @@ -76,11 +84,10 @@ public ListenableFuture<Void> execute(
}
TableHandle tableHandle = redirectionAwareTableHandle.getTableHandle().get();

String source = statement.getSource().getValue().toLowerCase(ENGLISH);
String source = statement.getSource().getParts().get(0).toLowerCase(ENGLISH);
String target = statement.getTarget().getValue().toLowerCase(ENGLISH);

QualifiedObjectName qualifiedTableName = redirectionAwareTableHandle.getRedirectedTableName().orElse(originalTableName);
accessControl.checkCanRenameColumn(session.toSecurityContext(), qualifiedTableName);

Map<String, ColumnHandle> columnHandles = metadata.getColumnHandles(session, tableHandle);
ColumnHandle columnHandle = columnHandles.get(source);
Expand All @@ -90,17 +97,72 @@ public ListenableFuture<Void> execute(
}
return immediateVoidFuture();
}

if (columnHandles.containsKey(target)) {
throw semanticException(COLUMN_ALREADY_EXISTS, statement, "Column '%s' already exists", target);
}

if (metadata.getColumnMetadata(session, tableHandle, columnHandle).isHidden()) {
throw semanticException(NOT_SUPPORTED, statement, "Cannot rename hidden column");
}

metadata.renameColumn(session, tableHandle, qualifiedTableName.asCatalogSchemaTableName(), columnHandle, target);
if (statement.getSource().getParts().size() == 1) {
accessControl.checkCanRenameColumn(session.toSecurityContext(), qualifiedTableName);

if (columnHandles.containsKey(target)) {
throw semanticException(COLUMN_ALREADY_EXISTS, statement, "Column '%s' already exists", target);
}

metadata.renameColumn(session, tableHandle, qualifiedTableName.asCatalogSchemaTableName(), columnHandle, target);
}
else {
accessControl.checkCanAlterColumn(session.toSecurityContext(), qualifiedTableName);

List<String> fieldPath = statement.getSource().getParts();

ColumnMetadata columnMetadata = metadata.getColumnMetadata(session, tableHandle, columnHandle);
Type currentType = columnMetadata.getType();
for (int i = 1; i < fieldPath.size() - 1; i++) {
String fieldName = fieldPath.get(i);
List<RowType.Field> candidates = getCandidates(currentType, fieldName);

if (candidates.isEmpty()) {
throw semanticException(COLUMN_NOT_FOUND, statement, "Field '%s' does not exist within %s", fieldName, currentType);
}
if (candidates.size() > 1) {
throw semanticException(AMBIGUOUS_NAME, statement, "Field path %s within %s is ambiguous", fieldPath, columnMetadata.getType());
}
currentType = getOnlyElement(candidates).getType();
}

String sourceFieldName = getLast(statement.getSource().getParts());
List<RowType.Field> sourceCandidates = getCandidates(currentType, sourceFieldName);
if (sourceCandidates.isEmpty()) {
if (!statement.isColumnExists()) {
throw semanticException(COLUMN_NOT_FOUND, statement, "Field '%s' does not exist", source);
}
return immediateVoidFuture();
}
if (sourceCandidates.size() > 1) {
throw semanticException(AMBIGUOUS_NAME, statement, "Field path %s within %s is ambiguous", fieldPath, columnMetadata.getType());
}

List<RowType.Field> targetCandidates = getCandidates(currentType, target);
if (!targetCandidates.isEmpty()) {
throw semanticException(COLUMN_ALREADY_EXISTS, statement, "Field '%s' already exists", target);
}

metadata.renameField(session, tableHandle, fieldPath, target);
}

return immediateVoidFuture();
}

private static List<RowType.Field> getCandidates(Type type, String fieldName)
{
if (!(type instanceof RowType rowType)) {
throw new TrinoException(NOT_SUPPORTED, "Unsupported type: " + type);
}
List<RowType.Field> candidates = rowType.getFields().stream()
// case-insensitive match
.filter(rowField -> rowField.getName().isPresent() && rowField.getName().get().equalsIgnoreCase(fieldName))
.collect(toImmutableList());

return candidates;
}
}
5 changes: 5 additions & 0 deletions core/trino-main/src/main/java/io/trino/metadata/Metadata.java
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ Optional<TableExecuteHandle> getTableHandleForExecute(
*/
void renameColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnHandle source, String target);

/**
* Rename the specified field.
*/
void renameField(Session session, TableHandle tableHandle, List<String> fieldPath, String target);

/**
* Add the specified column to the table.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,14 @@ public void renameColumn(Session session, TableHandle tableHandle, CatalogSchema
}
}

@Override
public void renameField(Session session, TableHandle tableHandle, List<String> fieldPath, String target)
{
CatalogHandle catalogHandle = tableHandle.getCatalogHandle();
ConnectorMetadata metadata = getMetadataForWrite(session, catalogHandle);
metadata.renameField(session.toConnectorSession(catalogHandle), tableHandle.getConnectorHandle(), fieldPath, target.toLowerCase(ENGLISH));
}

@Override
public void addColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnMetadata column)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,15 @@ public void renameColumn(ConnectorSession session, ConnectorTableHandle tableHan
}
}

@Override
public void renameField(ConnectorSession session, ConnectorTableHandle tableHandle, List<String> fieldPath, String target)
{
Span span = startSpan("renameField", tableHandle);
try (var ignored = scopedSpan(span)) {
delegate.renameField(session, tableHandle, fieldPath, target);
}
}

@Override
public void dropColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,15 @@ public void renameColumn(Session session, TableHandle tableHandle, CatalogSchema
}
}

@Override
public void renameField(Session session, TableHandle tableHandle, List<String> fieldPath, String target)
{
Span span = startSpan("renameField", tableHandle);
try (var ignored = scopedSpan(span)) {
delegate.renameField(session, tableHandle, fieldPath, target);
}
}

@Override
public void addColumn(Session session, TableHandle tableHandle, CatalogSchemaTableName table, ColumnMetadata column)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,12 @@ public void setTableAuthorization(ConnectorSession session, SchemaTableName tabl
@Override
public void renameColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle source, String target) {}

@Override
public void renameField(ConnectorSession session, ConnectorTableHandle tableHandle, List<String> fieldPath, String target)
{
throw new UnsupportedOperationException();
}

@Override
public void dropColumn(ConnectorSession session, ConnectorTableHandle tableHandle, ColumnHandle column) {}

Expand Down
Loading