apache/beam · error · java.lang.UnsupportedOperationException

FULL OUTER JOIN is not supported when join a Seekable table

Error message

FULL OUTER JOIN is not supported when join a Seekable table with a non Seekable table.

What it means

Beam SQL's side-input lookup join supports only INNER, LEFT OUTER (non-seekable on the left) and RIGHT OUTER (non-seekable on the right) joins between a Seekable table and a non-Seekable table. A FULL OUTER JOIN cannot be performed this way because it would require streaming the seekable side's unmatched rows, so the translation throws this UnsupportedOperationException.

Source

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

      RelOptCluster cluster,
      RelTraitSet traitSet,
      RelNode left,
      RelNode right,
      RexNode condition,
      Set<CorrelationId> variablesSet,
      JoinRelType joinType) {
    super(cluster, traitSet, left, right, condition, variablesSet, joinType);
  }

  @Override
  public PTransform<PCollectionList<Row>, PCollection<Row>> buildPTransform() {
    // if one of the sides is Seekable & the other is non Seekable
    // then do a sideInputLookup join.
    // When doing a sideInputLookup join, the windowFn does not need to match.
    // Only support INNER JOIN & LEFT OUTER JOIN where left side of the join must be
    // non Seekable & RIGHT OUTER JOIN where right side of the join must be non Seekable
    if (joinType == JoinRelType.FULL) {
      throw new UnsupportedOperationException(
          "FULL OUTER JOIN is not supported when join "
              + "a Seekable table with a non Seekable table.");
    }

    if ((joinType == JoinRelType.LEFT && seekableInputIndex().get() == 0)
        || (joinType == JoinRelType.RIGHT && seekableInputIndex().get() == 1)) {
      throw new UnsupportedOperationException(
          String.format("%s side of an OUTER JOIN must be a non Seekable table.", joinType.name()));
    }
    return new SideInputLookupJoin();
  }

  private class SideInputLookupJoin extends PTransform<PCollectionList<Row>, PCollection<Row>> {

    @Override
    public PCollection<Row> expand(PCollectionList<Row> pinput) {
      Schema schema = CalciteUtils.toSchema(getRowType());

View on GitHub (pinned to 12126d8942)

Solutions

  1. Rewrite as LEFT OUTER JOIN with the non-seekable table on the left, plus a complementary RIGHT-OUTER-derived query for unmatched seekable rows, and UNION ALL the results.
  2. Change the join type to INNER or LEFT/RIGHT OUTER, whichever matches which side is seekable.
  3. Materialize the seekable table as a bounded PCollection and use a regular (non-lookup) join that supports FULL OUTER JOIN.
  4. Implement a custom join transform (e.g. CoGroupByKey-based) if FULL OUTER semantics over both sides are truly required.

Example fix

-- before
SELECT * FROM non_seekable FULL OUTER JOIN seekable_table ON non_seekable.k = seekable_table.k
-- after
SELECT * FROM non_seekable LEFT OUTER JOIN seekable_table ON non_seekable.k = seekable_table.k
Defensive patterns

Strategy: validation

Validate before calling

if (joinType == JoinRelType.FULL && (isSeekable(left) ^ isSeekable(right))) { throw new IllegalArgumentException("FULL OUTER JOIN unsupported for mixed Seekable/non-Seekable sides"); }

Type guard

boolean lookupJoinAllowed = !(joinType == JoinRelType.FULL && exactlyOneSeekable);

Try / catch

try { pipeline.apply(SqlTransform.query(sql)); } catch (UnsupportedOperationException e) { if (e.getMessage().contains("FULL OUTER JOIN is not supported")) { /* decompose into LEFT OUTER + anti-join UNION ALL */ } else { throw e; } }

Prevention

When it happens

Trigger: Translating a BeamSql FULL OUTER JOIN query where exactly one side is a SeekableTable and the side-input lookup join rule (BeamSideInputLookupJoinRel) is chosen.

Common situations: Attempting FULL OUTER JOIN against a seekable side-input table such as a lookup backed by a stateful/seekable DoFn (e.g. a JDBC or BigTable-backed SeekableTable) in a streaming pipeline.

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