apache/beam · error · java.lang.UnsupportedOperationException
Non equi-join is not supported
Error message
Non equi-join is not supported
What it means
extractJoinPairOfRexNodes builds the (left, right) column pair for one conjunct of a join condition. If the conjunct's operator is not '=', the join is non-equi and Beam SQL throws UnsupportedOperationException, since its join implementations require key equality.
Source
Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamJoinRel.java:191
if ("AND".equals(call.getOperator().getName())) {
List<RexNode> operands = call.getOperands();
for (RexNode rexNode : operands) {
Pair<RexNode, RexNode> pair = extractJoinPairOfRexNodes((RexCall) rexNode);
pairs.add(pair);
}
} else if ("=".equals(call.getOperator().getName())) {
pairs.add(extractJoinPairOfRexNodes(call));
} else {
throw new UnsupportedOperationException(
"Operator " + call.getOperator().getName() + " is not supported in join condition");
}
return pairs;
}
private static Pair<RexNode, RexNode> extractJoinPairOfRexNodes(RexCall rexCall) {
if (!rexCall.getOperator().getName().equals("=")) {
throw new UnsupportedOperationException("Non equi-join is not supported");
}
if (isIllegalJoinConjunctionClause(rexCall)) {
throw new UnsupportedOperationException(
"Only support column reference or struct field access in conjunction clause");
}
int leftIndex = getColumnIndex(rexCall.getOperands().get(0));
int rightIndex = getColumnIndex(rexCall.getOperands().get(1));
if (leftIndex < rightIndex) {
return new Pair<>(rexCall.getOperands().get(0), rexCall.getOperands().get(1));
} else {
return new Pair<>(rexCall.getOperands().get(1), rexCall.getOperands().get(0));
}
}
// Only support {RexInputRef | RexFieldAccess} = {RexInputRef | RexFieldAccess}
private static boolean isIllegalJoinConjunctionClause(RexCall rexCall) {View on GitHub (pinned to 12126d8942)
Solutions
- Move non-equi predicates out of the ON clause into a WHERE filter applied after the equi-join
- Ensure every conjunct in the join condition is a column = column equality
- Pre-filter datasets or use side-input joins / DoFn-based joins for range semantics
Example fix
// before SELECT * FROM a JOIN b ON a.id = b.id AND a.ts <= b.ts AND b.te >= a.ts; // after SELECT * FROM a JOIN b ON a.id = b.id WHERE a.ts <= b.ts AND b.te >= a.ts;
Defensive patterns
Strategy: validation
Validate before calling
// Ensure every AND conjunct is an equality
for (RexNode conjunct : splitConjuncts(condition)) {
if (!(conjunct instanceof RexCall) || !"=".equals(((RexCall) conjunct).getOperator().getName())) {
throw new IllegalArgumentException("Non equi-join conjunct found; move it to WHERE");
}
} Try / catch
try {
result = sqlEnv.sqlQuery(q).evaluate();
} catch (UnsupportedOperationException e) {
if ("Non equi-join is not supported".equals(e.getMessage())) {
q = rewriteNonEquiToWhereFilter(q);
} else throw e;
} Prevention
- Split complex join conditions into equi-join ON plus WHERE filters
- Avoid theta-join patterns (range overlap conditions) in Beam SQL
- Test joins with representative conditions before production
When it happens
Trigger: An AND-combined join condition containing a non-equality conjunct, e.g. ON a.id = b.id AND a.ts < b.ts, where the non-equality conjunct reaches extractJoinPairOfRexNodes.
Common situations: Theta joins / temporal range conditions written directly in ON clauses; queries migrated from engines with full theta-join support.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- CROSS JOIN, JOIN ON FALSE is not supported!
- Operator ${operatorName} is not supported in join condition
- Only support column reference or struct field access in conj
- Cannot get column index from ${type}
- FULL OUTER JOIN is not supported when join a bounded table w
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/99a1ad241bc706d7.
Report an issue: GitHub.