apache/beam · error · java.lang.UnsupportedOperationException

Cannot get column index from ${type}

Error message

Cannot get column index from ${type}

What it means

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.

Source

Thrown at sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamJoinRel.java:223

    }
  }

  // Only support {RexInputRef | RexFieldAccess} = {RexInputRef | RexFieldAccess}
  private static boolean isIllegalJoinConjunctionClause(RexCall rexCall) {
    return (!(rexCall.getOperands().get(0) instanceof RexInputRef)
            && !(rexCall.getOperands().get(0) instanceof RexFieldAccess))
        || (!(rexCall.getOperands().get(1) instanceof RexInputRef)
            && !(rexCall.getOperands().get(1) instanceof RexFieldAccess));
  }

  private static int getColumnIndex(RexNode rexNode) {
    if (rexNode instanceof RexInputRef) {
      return ((RexInputRef) rexNode).getIndex();
    } else if (rexNode instanceof RexFieldAccess) {
      return getColumnIndex(((RexFieldAccess) rexNode).getReferenceExpr());
    }

    throw new UnsupportedOperationException("Cannot get column index from " + rexNode.getType());
  }

  /**
   * This method returns the Boundedness of a RelNode. It is used during planning and applying
   * {@link org.apache.beam.sdk.extensions.sql.impl.rule.BeamCoGBKJoinRule} and {@link
   * org.apache.beam.sdk.extensions.sql.impl.rule.BeamSideInputJoinRule}
   *
   * <p>The Volcano planner works in a top-down fashion. It starts by transforming the root and move
   * towards the leafs of the plan. Due to this when transforming a logical join its inputs are
   * still in the logical convention. So, Recursively visit the inputs of the RelNode till
   * BeamIOSourceRel is encountered and propagate the boundedness upwards.
   *
   * <p>The Boundedness of each child of a RelNode is stored in a list. If any of the children are
   * Unbounded, the RelNode is Unbounded. Else, the RelNode is Bounded.
   *
   * @param relNode the RelNode whose Boundedness has to be determined
   * @return {@code PCollection.isBounded}
   */

View on GitHub (pinned to 12126d8942)

Solutions

  1. Ensure both sides of each '=' conjunct are plain column references or struct field accesses
  2. Precompute derived join keys in a subquery/CTE so the join sees bare columns
  3. If extending Beam, add handling for additional RexNode kinds (e.g. RexCall with deterministic functions) in getColumnIndex

Example fix

// before
SELECT * FROM a JOIN b ON a.id + 1 = b.id;
// after
SELECT * FROM (SELECT id + 1 AS id_next FROM a) a JOIN b ON a.id_next = b.id;
Defensive patterns

Strategy: type-guard

Validate before calling

// Check join-key operands resolve to plain columns before building the query
boolean isPlainColumnOrField(String expr) {
  return expr.matches("\\w+") || expr.matches("\\w+(\\.\\w+)+"); // no function calls/casts/arithmetic
}

Type guard

static boolean isResolvableJoinKey(RexNode node) {
  return node instanceof RexInputRef
      || (node instanceof RexFieldAccess
          && isResolvableJoinKey(((RexFieldAccess) node).getReferenceExpr()));
}

Try / catch

try {
  result = sqlEnv.sqlQuery(q).evaluate();
} catch (UnsupportedOperationException e) {
  if (e.getMessage().startsWith("Cannot get column index from")) {
    q = projectJoinKeysFirst(q);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: Joining on expressions (concat, casts, arithmetic) or constants inside the ON clause; struct field access patterns deeper than what getColumnIndex recurses into.

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/a6672c7a1226882c. Report an issue: GitHub.