apache/seatunnel · error · ParserException

Insert sql must have select statement

Error message

Insert sql must have select statement

What it means

Validation in SqlConfigBuilder.parseInsertSql: an INSERT statement in the SQL config must be INSERT INTO ... SELECT; an insert whose body does not contain a Select (e.g. INSERT ... VALUES) cannot be translated into a SeaTunnel pipeline and is rejected with ParserException.

Source

Thrown at seatunnel-config/seatunnel-config-sql/src/main/java/org/apache/seatunnel/config/sql/SqlConfigBuilder.java:371

        } else {
            throw new ParserException(String.format("Unsupported syntax: %s", createTable));
        }
    }

    private static void parseInsertSql(
            Insert insertSql,
            Map<String, BaseConfig> sqlTables,
            SeaTunnelConfig seaTunnelConfig,
            AtomicInteger tempTableIndex) {
        if (insertSql.getColumns() != null && !insertSql.getColumns().isEmpty()) {
            throw new ParserException("Insert sql must not have columns");
        }
        TransformConfig transformConfig = new TransformConfig();
        Select select = insertSql.getSelect();
        if (select == null
                || select.getSelectBody() == null
                || !(select.getSelectBody() instanceof PlainSelect)) {
            throw new ParserException("Insert sql must have select statement");
        }
        String targetTableName = insertSql.getTable().getName();
        if (select.getSelectBody() instanceof PlainSelect) {
            PlainSelect plainSelect = (PlainSelect) select.getSelectBody();

            String pluginInputIdentifier;
            String pluginOutputIdentifier;
            if (plainSelect.getFromItem() == null) {
                List<SelectItem<?>> selectItems = plainSelect.getSelectItems();
                if (selectItems.size() != 1) {
                    throw new ParserException(
                            "Source table must be specified in SQL: " + insertSql);
                }
                SelectItem<?> selectItem = selectItems.get(0);
                Column column = (Column) selectItem.getExpression();
                pluginInputIdentifier = column.getColumnName();
                pluginOutputIdentifier = pluginInputIdentifier;
            } else {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Replace VALUES (...) with an equivalent 'SELECT ... ' from a source table or a fake/dataset source
  2. Rewrite UNION/CTE bodies into a single plain SELECT
  3. Ensure the INSERT statement is complete and ends with a SELECT clause

Example fix

// before
INSERT INTO sink_t VALUES (1, 'a')
// after
INSERT INTO sink_t SELECT 1, 'a' FROM src
Defensive patterns

Strategy: validation

Validate before calling

// ensure each INSERT has a plain SELECT body
for (String stmt : insertStatements(sql)) {
    if (!stmt.matches("(?si)INSERT\\s+INTO\\s+\\w+\\s+SELECT\\s+.*")) {
        throw new IllegalArgumentException("INSERT must be followed by a plain SELECT: " + stmt);
    }
}

Try / catch

try {
    SeaTunnelConfig c = SqlConfigBuilder.of(sql);
} catch (ParserException e) {
    if (e.getMessage().equals("Insert sql must have select statement")) {
        // replace VALUES/compound select with plain SELECT
    }
}

Prevention

When it happens

Trigger: INSERT INTO sink_t VALUES (...); INSERT ... with a UNION/CTE select body; truncated SQL missing the SELECT entirely.

Common situations: Porting row-value INSERT syntax from standard SQL; using multi-table or compound select statements the simple parser rejects.

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/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/ba3ec834f9ab0521. Report an issue: GitHub.