{"record":{"id":"99a1ad241bc706d7","repo":"apache/beam","slug":"non-equi-join-is-not-supported","errorCode":null,"errorMessage":"Non equi-join is not supported","messagePattern":"Non equi-join is not supported","errorType":"exception","errorClass":"java.lang.UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamJoinRel.java","lineNumber":191,"sourceCode":"    if (\"AND\".equals(call.getOperator().getName())) {\n      List<RexNode> operands = call.getOperands();\n      for (RexNode rexNode : operands) {\n        Pair<RexNode, RexNode> pair = extractJoinPairOfRexNodes((RexCall) rexNode);\n        pairs.add(pair);\n      }\n    } else if (\"=\".equals(call.getOperator().getName())) {\n      pairs.add(extractJoinPairOfRexNodes(call));\n    } else {\n      throw new UnsupportedOperationException(\n          \"Operator \" + call.getOperator().getName() + \" is not supported in join condition\");\n    }\n\n    return pairs;\n  }\n\n  private static Pair<RexNode, RexNode> extractJoinPairOfRexNodes(RexCall rexCall) {\n    if (!rexCall.getOperator().getName().equals(\"=\")) {\n      throw new UnsupportedOperationException(\"Non equi-join is not supported\");\n    }\n\n    if (isIllegalJoinConjunctionClause(rexCall)) {\n      throw new UnsupportedOperationException(\n          \"Only support column reference or struct field access in conjunction clause\");\n    }\n\n    int leftIndex = getColumnIndex(rexCall.getOperands().get(0));\n    int rightIndex = getColumnIndex(rexCall.getOperands().get(1));\n    if (leftIndex < rightIndex) {\n      return new Pair<>(rexCall.getOperands().get(0), rexCall.getOperands().get(1));\n    } else {\n      return new Pair<>(rexCall.getOperands().get(1), rexCall.getOperands().get(0));\n    }\n  }\n\n  // Only support {RexInputRef | RexFieldAccess} = {RexInputRef | RexFieldAccess}\n  private static boolean isIllegalJoinConjunctionClause(RexCall rexCall) {","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamJoinRel.java#L173-L209","documentation":"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.","triggerScenarios":"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.","commonSituations":"Theta joins / temporal range conditions written directly in ON clauses; queries migrated from engines with full theta-join support.","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"],"exampleFix":"// before\nSELECT * FROM a JOIN b ON a.id = b.id AND a.ts <= b.ts AND b.te >= a.ts;\n// after\nSELECT * FROM a JOIN b ON a.id = b.id WHERE a.ts <= b.ts AND b.te >= a.ts;","handlingStrategy":"validation","validationCode":"// Ensure every AND conjunct is an equality\nfor (RexNode conjunct : splitConjuncts(condition)) {\n  if (!(conjunct instanceof RexCall) || !\"=\".equals(((RexCall) conjunct).getOperator().getName())) {\n    throw new IllegalArgumentException(\"Non equi-join conjunct found; move it to WHERE\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  result = sqlEnv.sqlQuery(q).evaluate();\n} catch (UnsupportedOperationException e) {\n  if (\"Non equi-join is not supported\".equals(e.getMessage())) {\n    q = rewriteNonEquiToWhereFilter(q);\n  } else throw e;\n}","preventionTips":["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"],"tags":["sql","join","equi-join","beam"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}