prestodb/presto · error · PinotException

PINOT_UNSUPPORTED_EXPRESSION

PINOT_UNSUPPORTED_EXPRESSION

Error message

Expected Pinot column handle %s to occur only once, but we have: %s

What it means

When building the assignments-to-index map, the generator expects each PinotColumnHandle variable in the assignments map to appear exactly once. A duplicate key means the same column handle is assigned to multiple output positions, which Pinot's columnar output cannot disambiguate; the generator throws PINOT_UNSUPPORTED_EXPRESSION with the full assignment list.

Source

Thrown at presto-pinot-toolkit/src/main/java/com/facebook/presto/pinot/query/PinotQueryGeneratorContext.java:441

                        outputColumn,
                        Joiner.on(",").withKeyValueSeparator(":").join(selections)));
            }
            expressionsInPinotOrder.put(outputColumn, outputColumnDefinition);
        }

        checkSupported(
                assignments.size() <= expressionsInPinotOrder.keySet().stream().filter(key -> !hiddenColumnSet.contains(key)).count(),
                "Expected returned expressions %s is a superset of selections %s",
                Joiner.on(",").withKeyValueSeparator(":").join(expressionsInPinotOrder),
                Joiner.on(",").withKeyValueSeparator("=").join(assignments));

        Map<VariableReferenceExpression, Integer> assignmentToIndex = new HashMap<>();
        Iterator<Map.Entry<VariableReferenceExpression, PinotColumnHandle>> assignmentsIterator = assignments.entrySet().iterator();
        for (int i = 0; i < assignments.size(); i++) {
            VariableReferenceExpression key = assignmentsIterator.next().getKey();
            Integer previous = assignmentToIndex.put(key, i);
            if (previous != null) {
                throw new PinotException(PINOT_UNSUPPORTED_EXPRESSION, Optional.of(query), format("Expected Pinot column handle %s to occur only once, but we have: %s", key, Joiner.on(",").withKeyValueSeparator("=").join(assignments)));
            }
        }

        ImmutableList.Builder<Integer> outputIndices = ImmutableList.builder();
        for (Map.Entry<VariableReferenceExpression, Selection> expression : expressionsInPinotOrder.entrySet()) {
            Integer index;
            if (hiddenColumnSet.contains(expression.getKey())) {
                index = -1; // negative output index means to skip this value returned by pinot at query time
            }
            else {
                index = assignmentToIndex.getOrDefault(expression.getKey(), -1); // negative output index means to skip this value returned by pinot at query time
            }
            outputIndices.add(index);
        }
        return outputIndices.build();
    }

    public LinkedHashMap<VariableReferenceExpression, PinotColumnHandle> getAssignments()

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Remove duplicate column references in the SELECT list (select the column once and reuse it).
  2. Rewrite the query so each referenced column appears under a single variable/alias in the pushed plan.
  3. If a single-reference query triggers it, the assignment construction has a bug — report with the query and assignments from the message.

Example fix

// before
SELECT user_id, user_id AS uid FROM events; // duplicate column handle
// after
SELECT user_id FROM events;
Defensive patterns

Strategy: validation

Validate before calling

// Deduplicate column references in the SELECT list before running
List<String> deduped = selectTerms.stream().distinct().collect(Collectors.toList());
if (deduped.size() != selectTerms.size()) {
    selectTerms = deduped; // or alias via Presto-side projection instead
}

Try / catch

try {
    runQuery(sql);
} catch (PinotException e) {
    if (e.getMessage() != null && e.getMessage().contains("to occur only once")) {
        runQuery(deduplicatedSql);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: A plan where the same Pinot column handle is assigned to two different VariableReferenceExpressions (duplicate assignments), e.g. the same column selected twice through different variable names or duplicated by a join/union rewrite before pushdown.

Common situations: Queries selecting the same column multiple times with different aliases combined with pushdown; planner rewrites (dedup/pruning) producing shared handles; connector version changes in assignment keying.

Related errors


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