{"record":{"id":"dd4cf0bf1b8e3c72","repo":"prestodb/presto","slug":"duplicate-column-name-dd4cf0","errorCode":"DUPLICATE_COLUMN_NAME","errorMessage":"Duplicate field name '%s' in ROW","messagePattern":"Duplicate field name '(.+?)' in ROW","errorType":"error_code","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java","lineNumber":447,"sourceCode":"            Set<String> declaredNames = new HashSet<>();\n            for (Row.Field field : node.getFields()) {\n                Type fieldType = process(field.getExpression(), context);\n                Optional<Identifier> name = field.getName();\n                if (!name.isPresent()) {\n                    fields.add(RowType.field(fieldType));\n                    continue;\n                }\n                // Fold the name the same way CAST(... AS ROW(...)) does. Cast.transformCase\n                // lowercases everything outside double quotes, so an undelimited name is folded to\n                // lower case and a delimited one is kept verbatim. Doing the same here keeps\n                // ROW(1 AS Abc) and CAST(ROW(1) AS ROW(Abc integer)) producing the same type.\n                // Note this is deliberately not getCanonicalValue(), which upper cases instead.\n                Identifier identifier = name.get();\n                String fieldName = identifier.isDelimited() ? identifier.getValue() : identifier.getValueLowerCase();\n                // Duplicates are rejected because a row type with repeated field names cannot be\n                // round-tripped through its TypeSignature, which the native worker relies on.\n                if (!declaredNames.add(fieldName)) {\n                    throw new SemanticException(DUPLICATE_COLUMN_NAME, node, \"Duplicate field name '%s' in ROW\", fieldName);\n                }\n                fields.add(RowType.field(fieldName, fieldType, identifier.isDelimited()));\n            }\n\n            return setExpressionType(node, RowType.from(fields.build()));\n        }\n\n        @Override\n        protected Type visitCurrentTime(CurrentTime node, StackableAstVisitorContext<Context> context)\n        {\n            if (node.getPrecision() != null) {\n                throw new SemanticException(NOT_SUPPORTED, node, \"non-default precision not yet supported\");\n            }\n\n            Type type;\n            switch (node.getFunction()) {\n                case DATE:\n                    type = DATE;","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/ExpressionAnalyzer.java#L429-L465","documentation":"visitRow rejects a ROW type literal whose field names repeat. Duplicates are disallowed because a row type with repeated field names cannot be round-tripped through its TypeSignature, which the native worker relies on. The check uses the delimited or lowercased identifier value when comparing names.","triggerScenarios":"Writing a ROW literal or CAST to ROW with two fields sharing the same name, e.g. CAST(ROW(1,2) AS ROW(a INTEGER, a VARCHAR)) or ROW('x' AS k, 'y' AS k).","commonSituations":"Hand-written SQL with copy-pasted field lists, generated SQL from ORMs producing duplicate aliases, schema evolution tools emitting a ROW type with duplicated column names.","solutions":["Rename the duplicated field in the ROW type definition","Unquote deliberately-cased names so they differ, or change casing so the lowercased names differ","Regenerate the SQL/schema so column aliases are unique"],"exampleFix":"// before\nCAST(ROW(1, 'x') AS ROW(a INTEGER, a VARCHAR))\n// after\nCAST(ROW(1, 'x') AS ROW(a INTEGER, b VARCHAR))","handlingStrategy":"validation","validationCode":"Set<String> names = rowFields.stream()\n    .map(f -> f.getName().toLowerCase())\n    .collect(Collectors.toSet());\nif (names.size() != rowFields.size()) throw new IllegalArgumentException(\"Duplicate ROW field names\");","typeGuard":null,"tryCatchPattern":"try {\n    return session.execute(sql);\n} catch (SemanticException e) {\n    if (e.getCode() == DUPLICATE_COLUMN_NAME) {\n        // auto-rename or report to user with offending field\n    } else throw e;\n}","preventionTips":["Always give ROW fields explicit unique names","Deduplicate aliases when generating ROW types programmatically","Add a schema lint step that checks ROW type definitions for duplicate names","Remember comparisons are on delimited value or lowercased name — casing cannot hide duplicates"],"tags":["sql","row-type","semantic"],"backgroundTag":"duplicate-field-name","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}