{"record":{"id":"fb0d51809dea8734","repo":"apache/beam","slug":"does-not-support-beamiosinkrel-in-torowlist","errorCode":null,"errorMessage":"Does not support BeamIOSinkRel in toRowList.","messagePattern":"Does not support BeamIOSinkRel in toRowList\\.","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/BeamEnumerableConverter.java","lineNumber":160,"sourceCode":"      Thread.currentThread().setContextClassLoader(originalClassLoader);\n    }\n  }\n\n  public static PipelineOptions createPipelineOptions(Map<String, String> map) {\n    final String[] args = new String[map.size()];\n    int i = 0;\n    for (Map.Entry<String, String> entry : map.entrySet()) {\n      args[i++] = \"--\" + entry.getKey() + \"=\" + entry.getValue();\n    }\n    PipelineOptions options = PipelineOptionsFactory.fromArgs(args).withValidation().create();\n    FileSystems.setDefaultPipelineOptions(options);\n    options.as(ApplicationNameOptions.class).setAppName(\"BeamSql\");\n    return options;\n  }\n\n  static List<Row> toRowList(PipelineOptions options, BeamRelNode node) {\n    if (node instanceof BeamIOSinkRel) {\n      throw new UnsupportedOperationException(\"Does not support BeamIOSinkRel in toRowList.\");\n    } else if (isLimitQuery(node)) {\n      throw new UnsupportedOperationException(\"Does not support queries with LIMIT in toRowList.\");\n    }\n    return collectRows(options, node).stream().collect(Collectors.toList());\n  }\n\n  static Enumerable<Object> toEnumerable(PipelineOptions options, BeamRelNode node) {\n    if (node instanceof BeamIOSinkRel) {\n      return count(options, node);\n    } else if (isLimitQuery(node)) {\n      return limitCollect(options, node);\n    }\n    return Linq4j.asEnumerable(rowToAvaticaAndUnboxValues(collectRows(options, node)));\n  }\n\n  private static PipelineResult limitRun(\n      PipelineOptions options,\n      BeamRelNode node,","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/sql/src/main/java/org/apache/beam/sdk/extensions/sql/impl/rel/BeamEnumerableConverter.java#L142-L178","documentation":"BeamEnumerableConverter.toRowList executes a Beam SQL plan and materializes results into an in-memory List<Row>. Sink rel nodes (BeamIOSinkRel, e.g. INSERT/CTAS-style statements that write to a table) do not produce enumerable rows, so calling toRowList on such a plan throws UnsupportedOperationException. The caller should instead use a path that executes the pipeline (such as toEnumerable/BeamSqlEnv execution) rather than collecting rows.","triggerScenarios":"Calling BeamEnumerableConverter.toRowList(options, node) (directly or via the JDBC/Shell default row-collection path) with a parsed and validated BeamRelNode that is a BeamIOSinkRel — i.e. the SQL statement is a DML sink (e.g. INSERT INTO) rather than a SELECT.","commonSituations":"Executing 'INSERT INTO ... SELECT ...' style statements through a code path that expects to collect rows in memory; using BeamSqlCli or the JDBC adapter configured to return row lists against a write statement; programmatically running sqlEnv.parseQuery(...).compile... and choosing toRowList for a non-SELECT statement.","solutions":["Use toEnumerable (or the sink-execution path) for BeamIOSinkRel plans instead of toRowList","Restrict toRowList usage to pure SELECT queries; detect DML beforehand (e.g. check the parsed statement type) and route write statements to pipeline execution","If you only need the side effect of writing, run the pipeline via PipelineResult waitUntilFinish after building from the sink rel, and ignore/empty row results","Check node instanceof BeamIOSinkRel in your own wrapper before calling toRowList and fail fast with a clearer message"],"exampleFix":"// before\nList<Row> rows = BeamEnumerableConverter.toRowList(options, relNode); // throws for INSERT\n// after\nif (relNode instanceof BeamIOSinkRel) {\n  BeamEnumerableConverter.toEnumerable(options, relNode). enumerator-move/execute pipeline; // execute sink\n} else {\n  List<Row> rows = BeamEnumerableConverter.toRowList(options, relNode);\n}","handlingStrategy":"type-guard","validationCode":"if (relNode instanceof BeamIOSinkRel) {\n  throw new IllegalArgumentException(\"Use pipeline/sink execution, not toRowList, for DML statements\");\n}","typeGuard":"boolean isSinkQuery(BeamRelNode node) { return node instanceof BeamIOSinkRel; }","tryCatchPattern":"try {\n  rows = BeamEnumerableConverter.toRowList(options, node);\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"BeamIOSinkRel\")) {\n    BeamEnumerableConverter.toEnumerable(options, node); // execute sink path\n  } else { throw e; }\n}","preventionTips":["Only call toRowList for SELECT-style plans; route INSERT/write statements to toEnumerable or pipeline execution","Check the parsed SQL statement type (DML vs query) before choosing an execution path","Inspect the plan tree for BeamIOSinkRel before materializing rows","Keep a wrapper API that picks the right execution mode automatically"],"tags":["java","beam-sql","unsupported-operation","dml","execution"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}