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

  1. Move non-equi predicates out of the ON clause into a WHERE filter applied after the equi-join
  2. Ensure every conjunct in the join condition is a column = column equality
  3. 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

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


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/99a1ad241bc706d7. Report an issue: GitHub.