{"record":{"id":"750a5ab09e83463f","repo":"apache/beam","slug":"only-support-column-reference-or-struct-field-access-in","errorCode":null,"errorMessage":"Only support column reference or struct field access in conjunction clause","messagePattern":"Only support column reference or struct field access in conjunction clause","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":195,"sourceCode":"        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) {\n    return (!(rexCall.getOperands().get(0) instanceof RexInputRef)\n            && !(rexCall.getOperands().get(0) instanceof RexFieldAccess))\n        || (!(rexCall.getOperands().get(1) instanceof RexInputRef)\n            && !(rexCall.getOperands().get(1) instanceof RexFieldAccess));","sourceCodeStart":177,"sourceCodeEnd":213,"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#L177-L213","documentation":"After confirming an '=' conjunct, extractJoinPairOfRexNodes checks that both operands are legal join keys: plain column references (RexInputRef) or struct field accesses. Anything else (literals, function calls, casts) in the join clause is rejected with UnsupportedOperationException.","triggerScenarios":"Join conditions containing expressions rather than bare columns, e.g. ON UPPER(a.name) = b.name or ON a.id = CAST(b.id AS BIGINT) or a constant on one side.","commonSituations":"Joining on computed/normalized values; comparing a join key to a literal; applying type casts inside the join condition.","solutions":["Join on raw columns only; apply functions/casts in a subquery or CTE before the join (e.g. project UPPER(name) AS name_norm then join on name_norm)","Remove literals from the join clause and express them as WHERE filters","Cast one of the source tables (via a subquery) so both key columns share the same type without in-condition casts"],"exampleFix":"// before\nSELECT * FROM a JOIN b ON UPPER(a.name) = b.name;\n// after\nSELECT * FROM (SELECT UPPER(name) AS name_norm, * FROM a) a JOIN b ON a.name_norm = b.name;","handlingStrategy":"validation","validationCode":"// Every join operand must be a column ref or struct field access\nfor (RexNode operand : joinKeyOperands) {\n  if (!(operand instanceof RexInputRef || operand instanceof RexFieldAccess)) {\n    throw new IllegalArgumentException(\"Join keys must be plain columns or struct fields\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  result = sqlEnv.sqlQuery(q).evaluate();\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"Only support column reference or struct field access\")) {\n    q = precomputeJoinKeysInSubquery(q);\n  } else throw e;\n}","preventionTips":["Never put functions, casts, or literals in join ON clauses","Normalize keys in subqueries/CTEs before joining","Keep both join-key columns the same type at the source"],"tags":["sql","join","expression","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"}