{"record":{"id":"9772a760b0469203","repo":"prestodb/presto","slug":"table-function-duplicate-range-variable","errorCode":"TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE","errorMessage":"Relation alias: %s is a duplicate of input table name: %s","messagePattern":"Relation alias: (.+?) is a duplicate of input table name: (.+?)","errorType":"error_code","errorClass":"SemanticException","httpStatus":null,"severity":"error","filePath":"presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java","lineNumber":3103,"sourceCode":"\n            return createAndAssignScope(relation, scope, descriptor);\n        }\n\n        // As described by the SQL standard ISO/IEC 9075-2, 7.6 <table reference>, p. 409\n        private RelationType aliasTableFunctionInvocation(AliasedRelation relation, RelationType relationType, TableFunctionInvocation function)\n        {\n            TableFunctionInvocationAnalysis tableFunctionAnalysis = analysis.getTableFunctionAnalysis(function);\n            int properColumnsCount = tableFunctionAnalysis.getProperColumnsCount();\n\n            // check that relation alias is different from range variables of all table arguments\n            tableFunctionAnalysis.getTableArgumentAnalyses().stream()\n                    .map(TableArgumentAnalysis::getName)\n                    .filter(Optional::isPresent)\n                    .map(Optional::get)\n                    .filter(name -> name.hasSuffix(QualifiedName.of(ImmutableList.of(relation.getAlias()))))\n                    .findFirst()\n                    .ifPresent(name -> {\n                        throw new SemanticException(TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE, relation.getAlias(), \"Relation alias: %s is a duplicate of input table name: %s\", relation.getAlias(), name);\n                    });\n\n            // build the new relation type. the alias must be applied to the proper columns only,\n            // and it must not shadow the range variables exposed by the table arguments\n            ImmutableList.Builder<Field> fieldsBuilder = ImmutableList.builder();\n            // first, put the table function's proper columns with alias\n            if (relation.getColumnNames() != null) {\n                // check that number of column aliases matches number of table function's proper columns\n                if (properColumnsCount != relation.getColumnNames().size()) {\n                    throw new SemanticException(MISMATCHED_COLUMN_ALIASES, relation, \"Column alias list has %s entries but table function has %s proper columns\", relation.getColumnNames().size(), properColumnsCount);\n                }\n                for (int i = 0; i < properColumnsCount; i++) {\n                    // proper columns are not hidden, so we don't need to skip hidden fields\n                    Field field = relationType.getFieldByIndex(i);\n                    fieldsBuilder.add(Field.newQualified(\n                            field.getNodeLocation(),\n                            QualifiedName.of(ImmutableList.of(relation.getAlias())),\n                            Optional.of(relation.getColumnNames().get(i).getCanonicalValue()), // although the canonical name is recorded, fields are resolved case-insensitive","sourceCodeStart":3085,"sourceCodeEnd":3121,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java#L3085-L3121","documentation":"For a table function invocation with a relation alias, Presto checks that the alias does not duplicate any of the function's input table (range variable) names. The alias would shadow the range variables exposed by the table arguments, making those parameter columns unreferenceable, so the analyzer rejects it with TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE.","triggerScenarios":"Calling a polymorphic table function like TABLE(my_func(TABLE(t) AS t)) where the outer relation alias is the same name as an input table argument, e.g. FROM TABLE(f(TABLE(x) AS x)) AS x.","commonSituations":"Copy-paste of function examples where the alias coincides with the input table name; auto-generated SQL assigning the function name/table name as alias; refactoring that renames an input table without renaming the alias.","solutions":["Rename the relation alias to something distinct from every input table argument name","Keep the input table's range variable name and give the invocation a fresh alias","Drop the explicit alias if original column names are acceptable"],"exampleFix":"-- before\nSELECT * FROM TABLE(f(TABLE(orders) AS orders)) AS orders\n-- after\nSELECT * FROM TABLE(f(TABLE(orders) AS orders)) AS f_result","handlingStrategy":"validation","validationCode":"Set<String> inputNames = tableFunctionInvocation.getInputTableNames();\nif (inputNames.contains(alias)) {\n    throw new IllegalArgumentException(\"alias \" + alias + \" collides with input table name\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return client.execute(sql);\n} catch (SemanticException e) {\n    if (e.getCode() == SemanticErrorCode.TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE) {\n        sql = renameRelationAlias(sql); // pick a non-colliding alias and retry\n    } else { throw e; }\n}","preventionTips":["Use distinct prefixes for relation aliases (e.g. f_) vs input table range variables","Document the input table names of each table function used","Add alias-collision checks in SQL templating code","Avoid naming the invocation alias after the underlying tables"],"tags":["presto","sql","table-function","alias-collision"],"backgroundTag":"duplicate-alias-shadows-table-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"}