{"record":{"id":"a6672c7a1226882c","repo":"apache/beam","slug":"cannot-get-column-index-from-type","errorCode":null,"errorMessage":"Cannot get column index from ${type}","messagePattern":"Cannot get column index from (.+?)","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":223,"sourceCode":"    }\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));\n  }\n\n  private static int getColumnIndex(RexNode rexNode) {\n    if (rexNode instanceof RexInputRef) {\n      return ((RexInputRef) rexNode).getIndex();\n    } else if (rexNode instanceof RexFieldAccess) {\n      return getColumnIndex(((RexFieldAccess) rexNode).getReferenceExpr());\n    }\n\n    throw new UnsupportedOperationException(\"Cannot get column index from \" + rexNode.getType());\n  }\n\n  /**\n   * This method returns the Boundedness of a RelNode. It is used during planning and applying\n   * {@link org.apache.beam.sdk.extensions.sql.impl.rule.BeamCoGBKJoinRule} and {@link\n   * org.apache.beam.sdk.extensions.sql.impl.rule.BeamSideInputJoinRule}\n   *\n   * <p>The Volcano planner works in a top-down fashion. It starts by transforming the root and move\n   * towards the leafs of the plan. Due to this when transforming a logical join its inputs are\n   * still in the logical convention. So, Recursively visit the inputs of the RelNode till\n   * BeamIOSourceRel is encountered and propagate the boundedness upwards.\n   *\n   * <p>The Boundedness of each child of a RelNode is stored in a list. If any of the children are\n   * Unbounded, the RelNode is Unbounded. Else, the RelNode is Bounded.\n   *\n   * @param relNode the RelNode whose Boundedness has to be determined\n   * @return {@code PCollection.isBounded}\n   */","sourceCodeStart":205,"sourceCodeEnd":241,"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#L205-L241","documentation":"getColumnIndex resolves a join-key operand to an input column index, supporting RexInputRef and RexFieldAccess (recursive). Any other RexNode kind, or an operand whose type cannot be resolved to a column, triggers UnsupportedOperationException with the node's type in the message.","triggerScenarios":"A join predicate operand that is neither a plain column reference nor a struct field access — literals, RexCall expressions, or nested unsupported field-access shapes — passed into getColumnIndex from extractJoinPairOfRexNodes.","commonSituations":"Joining on expressions (concat, casts, arithmetic) or constants inside the ON clause; struct field access patterns deeper than what getColumnIndex recurses into.","solutions":["Ensure both sides of each '=' conjunct are plain column references or struct field accesses","Precompute derived join keys in a subquery/CTE so the join sees bare columns","If extending Beam, add handling for additional RexNode kinds (e.g. RexCall with deterministic functions) in getColumnIndex"],"exampleFix":"// before\nSELECT * FROM a JOIN b ON a.id + 1 = b.id;\n// after\nSELECT * FROM (SELECT id + 1 AS id_next FROM a) a JOIN b ON a.id_next = b.id;","handlingStrategy":"type-guard","validationCode":"// Check join-key operands resolve to plain columns before building the query\nboolean isPlainColumnOrField(String expr) {\n  return expr.matches(\"\\\\w+\") || expr.matches(\"\\\\w+(\\\\.\\\\w+)+\"); // no function calls/casts/arithmetic\n}","typeGuard":"static boolean isResolvableJoinKey(RexNode node) {\n  return node instanceof RexInputRef\n      || (node instanceof RexFieldAccess\n          && isResolvableJoinKey(((RexFieldAccess) node).getReferenceExpr()));\n}","tryCatchPattern":"try {\n  result = sqlEnv.sqlQuery(q).evaluate();\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().startsWith(\"Cannot get column index from\")) {\n    q = projectJoinKeysFirst(q);\n  } else throw e;\n}","preventionTips":["Use bare column references as join keys","Compute derived keys in a preceding projection","Recursively validate struct-field join keys match what getColumnIndex supports"],"tags":["sql","join","column-index","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"}