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
- Remove duplicate column references in the SELECT list (select the column once and reuse it).
- Rewrite the query so each referenced column appears under a single variable/alias in the pushed plan.
- 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
- Select each column at most once in Pinot-pushed queries; alias client-side.
- Avoid cross-join/union rewrites that share one column handle for multiple outputs.
- Review planner rewrites that duplicate assignments before pushdown.
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
- PINOT_UNSUPPORTED_EXPRESSION
- PINOT_UNSUPPORTED_EXPRESSION
- PINOT_UNSUPPORTED_EXPRESSION
- DRUID_PUSHDOWN_UNSUPPORTED_EXPRESSION
- PINOT_UNSUPPORTED_EXPRESSION
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/db580e79eecaf862.
Report an issue: GitHub.