prestodb/presto · error · SemanticException

DUPLICATE_COLUMN_NAME

DUPLICATE_COLUMN_NAME

Error message

Column name '%s' specified more than once

What it means

CreateTableTask.internalExecute throws SemanticException(DUPLICATE_COLUMN_NAME) when two column definitions normalize to the same name (columns.containsKey(name) after metadata.normalizeIdentifier). Duplicate column names make the resulting table schema ambiguous, so creation is rejected.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/execution/CreateTableTask.java:139

        boolean includingProperties = false;
        List<TableConstraint<String>> constraints = new ArrayList<>();
        for (TableElement element : statement.getElements()) {
            if (element instanceof ColumnDefinition) {
                ColumnDefinition column = (ColumnDefinition) element;
                String columnName = column.getName().getValue();
                String name = metadata.normalizeIdentifier(session, tableName.getCatalogName(), columnName);
                Type type;
                try {
                    type = metadata.getType(parseTypeSignature(column.getType()));
                }
                catch (IllegalArgumentException | UnknownTypeException e) {
                    throw new SemanticException(TYPE_MISMATCH, element, "Unknown type '%s' for column '%s'", column.getType(), column.getName());
                }
                if (type.equals(UNKNOWN)) {
                    throw new SemanticException(TYPE_MISMATCH, element, "Unknown type '%s' for column '%s'", column.getType(), column.getName());
                }
                if (columns.containsKey(name)) {
                    throw new SemanticException(DUPLICATE_COLUMN_NAME, column, "Column name '%s' specified more than once", column.getName());
                }
                if (!column.isNullable() && !metadata.getConnectorCapabilities(session, connectorId).contains(NOT_NULL_COLUMN_CONSTRAINT)) {
                    throw new SemanticException(NOT_SUPPORTED, column, "Catalog '%s' does not support non-null column for column name '%s'", connectorId.getCatalogName(), column.getName());
                }

                Map<String, Expression> sqlProperties = mapFromProperties(column.getProperties());
                Map<String, Object> columnProperties = metadata.getColumnPropertyManager().getProperties(
                        connectorId,
                        tableName.getCatalogName(),
                        sqlProperties,
                        session,
                        metadata,
                        parameterLookup);

                columns.put(name, ColumnMetadata.builder()
                        .setName(name)
                        .setType(type)
                        .setNullable(column.isNullable())

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Remove or rename the duplicate column so all names are unique after normalization.
  2. De-duplicate generated column lists before emitting DDL (e.g. LinkedHashMap keyed by normalized name).
  3. Check identifier normalization rules of the target catalog to avoid case-collisions.
  4. Validate the column list for duplicates before submitting the statement.

Example fix

-- before
CREATE TABLE t (id BIGINT, ID VARCHAR);

-- after
CREATE TABLE t (id BIGINT, id_label VARCHAR);
Defensive patterns

Strategy: validation

Validate before calling

Map<String, Boolean> seen = new LinkedHashMap<>();
for (ColumnDefinition c : statement.getElements()) {
    String name = metadata.normalizeIdentifier(session, catalog, c.getName());
    if (!seen.putIfAbsent(name, true) == null) {
        throw new IllegalStateException("Duplicate column after normalization: " + name);
    }
}

Type guard

List<String> duplicateColumns(Session session, Metadata metadata, String catalog, List<ColumnDefinition> columns) {
    Set<String> seen = new HashSet<>();
    return columns.stream()
        .map(c -> metadata.normalizeIdentifier(session, catalog, c.getName()))
        .filter(n -> !seen.add(n))
        .collect(Collectors.toList());
}

Try / catch

try {
    createTable(session, statement, parameters);
} catch (SemanticException e) {
    if (e.getCode() == DUPLICATE_COLUMN_NAME) {
        // de-duplicate/rename columns and resubmit
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: CREATE TABLE listing the same column name twice, or two names that normalize to the same key (case-insensitive/normalized identifiers, e.g. 'Id' and 'ID').

Common situations: Copy-pasted column lists that duplicate a column; generated DDL merging multiple sources with overlapping names; case-differing names in case-insensitive catalogs.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/dac4bf0dd8b17635. Report an issue: GitHub.