prestodb/presto · error · SemanticException

TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE

TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE

Error message

Relation alias: %s is a duplicate of input table name: %s

What it means

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.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java:3103

            return createAndAssignScope(relation, scope, descriptor);
        }

        // As described by the SQL standard ISO/IEC 9075-2, 7.6 <table reference>, p. 409
        private RelationType aliasTableFunctionInvocation(AliasedRelation relation, RelationType relationType, TableFunctionInvocation function)
        {
            TableFunctionInvocationAnalysis tableFunctionAnalysis = analysis.getTableFunctionAnalysis(function);
            int properColumnsCount = tableFunctionAnalysis.getProperColumnsCount();

            // check that relation alias is different from range variables of all table arguments
            tableFunctionAnalysis.getTableArgumentAnalyses().stream()
                    .map(TableArgumentAnalysis::getName)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .filter(name -> name.hasSuffix(QualifiedName.of(ImmutableList.of(relation.getAlias()))))
                    .findFirst()
                    .ifPresent(name -> {
                        throw new SemanticException(TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE, relation.getAlias(), "Relation alias: %s is a duplicate of input table name: %s", relation.getAlias(), name);
                    });

            // build the new relation type. the alias must be applied to the proper columns only,
            // and it must not shadow the range variables exposed by the table arguments
            ImmutableList.Builder<Field> fieldsBuilder = ImmutableList.builder();
            // first, put the table function's proper columns with alias
            if (relation.getColumnNames() != null) {
                // check that number of column aliases matches number of table function's proper columns
                if (properColumnsCount != relation.getColumnNames().size()) {
                    throw new SemanticException(MISMATCHED_COLUMN_ALIASES, relation, "Column alias list has %s entries but table function has %s proper columns", relation.getColumnNames().size(), properColumnsCount);
                }
                for (int i = 0; i < properColumnsCount; i++) {
                    // proper columns are not hidden, so we don't need to skip hidden fields
                    Field field = relationType.getFieldByIndex(i);
                    fieldsBuilder.add(Field.newQualified(
                            field.getNodeLocation(),
                            QualifiedName.of(ImmutableList.of(relation.getAlias())),
                            Optional.of(relation.getColumnNames().get(i).getCanonicalValue()), // although the canonical name is recorded, fields are resolved case-insensitive

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Rename the relation alias to something distinct from every input table argument name
  2. Keep the input table's range variable name and give the invocation a fresh alias
  3. Drop the explicit alias if original column names are acceptable

Example fix

-- before
SELECT * FROM TABLE(f(TABLE(orders) AS orders)) AS orders
-- after
SELECT * FROM TABLE(f(TABLE(orders) AS orders)) AS f_result
Defensive patterns

Strategy: validation

Validate before calling

Set<String> inputNames = tableFunctionInvocation.getInputTableNames();
if (inputNames.contains(alias)) {
    throw new IllegalArgumentException("alias " + alias + " collides with input table name");
}

Try / catch

try {
    return client.execute(sql);
} catch (SemanticException e) {
    if (e.getCode() == SemanticErrorCode.TABLE_FUNCTION_DUPLICATE_RANGE_VARIABLE) {
        sql = renameRelationAlias(sql); // pick a non-colliding alias and retry
    } else { throw e; }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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