prestodb/presto · error · IllegalArgumentException
Unsupported join criteria
Error message
Unsupported join criteria
What it means
AstBuilder.visitJoin throws this IllegalArgumentException when a JOIN clause has a joinCriteria() that is neither ON <booleanExpression> nor USING (...). The parser only recognizes those two criteria forms, so anything else (e.g. a criteria context with all children null, possible with hand-modified parse trees or grammar drift) reaches the else branch.
Source
Thrown at presto-parser/src/main/java/com/facebook/presto/sql/parser/AstBuilder.java:1651
right = (Relation) visit(context.right);
return new Join(getLocation(context), Join.Type.CROSS, left, right, Optional.empty());
}
JoinCriteria criteria;
if (context.NATURAL() != null) {
right = (Relation) visit(context.right);
criteria = new NaturalJoin();
}
else {
right = (Relation) visit(context.rightRelation);
if (context.joinCriteria().ON() != null) {
criteria = new JoinOn((Expression) visit(context.joinCriteria().booleanExpression()));
}
else if (context.joinCriteria().USING() != null) {
criteria = new JoinUsing(visit(context.joinCriteria().identifier(), Identifier.class));
}
else {
throw new IllegalArgumentException("Unsupported join criteria");
}
}
Join.Type joinType;
if (context.joinType().LEFT() != null) {
joinType = Join.Type.LEFT;
}
else if (context.joinType().RIGHT() != null) {
joinType = Join.Type.RIGHT;
}
else if (context.joinType().FULL() != null) {
joinType = Join.Type.FULL;
}
else {
joinType = Join.Type.INNER;
}
return new Join(getLocation(context), joinType, left, right, Optional.of(criteria));View on GitHub (pinned to 55bb57d202)
Solutions
- Rewrite the join using ON <condition> or USING (col1, col2, ...).
- Replace NATURAL JOIN with an explicit ON clause comparing the shared columns.
- If building parse trees programmatically, ensure joinCriteria() is one of the grammar's recognized forms.
- In a fork, extend visitJoin to handle the new criteria kind.
Example fix
// before SELECT * FROM a NATURAL JOIN b; // after SELECT * FROM a JOIN b ON a.id = b.id;
Defensive patterns
Strategy: validation
Validate before calling
// ensure every JOIN in the SQL text uses ON or USING
if (Pattern.compile("(?i)\\bJOIN\\b(?!\\s+(?:ON|USING|\\())").matcher(sql).find()) {
throw new IllegalArgumentException("JOIN must use ON or USING criteria");
} Type guard
boolean hasJoinCriteria(String joinClauseSql) {
return Pattern.compile("(?i)JOIN\\s+(\\S+\\s+)?(ON|USING)\\s").matcher(joinClauseSql).find();
} Try / catch
try {
parser.createStatement(sql);
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Unsupported join criteria")) {
// rewrite NATURAL/custom joins into explicit ON clauses
sql = rewriteNaturalJoins(sql);
parser.createStatement(sql);
} else throw e;
} Prevention
- Avoid NATURAL JOIN; always write explicit ON/USING.
- Keep custom grammar changes and visitJoin in sync.
- Lint generated SQL for join criteria before parsing.
When it happens
Trigger: Parsing a JOIN whose criteria token was altered or removed from the parse tree, or a custom grammar change that introduces a new criteria form (e.g. NATURAL variants) without updating visitJoin.
Common situations: Forked grammars, programmatic AST construction via the parser's generated contexts, or SQL dialects using join criteria keywords Presto does not support.
Related errors
- Unsupported EXPLAIN format:
- Unsupported EXPLAIN type:
- WARNING_AS_ERROR
- Invalid day-time interval:
- Invalid year-month interval:
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/7b4251d8041de75d.
Report an issue: GitHub.