apache/seatunnel · error · IllegalArgumentException

oracle_insert_mode=APPEND_VALUES only supports generated INS

Error message

oracle_insert_mode=APPEND_VALUES only supports generated INSERT statements.

What it means

Thrown by JdbcOutputFormatBuilder.applyOracleAppendValuesHintIfNeeded when oracle_insert_mode=APPEND_VALUES is configured but the generated SQL does not start with INSERT. The APPEND_VALUES hint can only be injected into a plain INSERT statement, so any other statement shape is rejected as an IllegalArgumentException.

Source

Thrown at seatunnel-connectors-v2/connector-jdbc/src/main/java/org/apache/seatunnel/connectors/seatunnel/jdbc/internal/JdbcOutputFormatBuilder.java:365

            TableSchema databaseTableSchema,
            JdbcRowConverter rowConverter) {
        return new SimpleBatchStatementExecutor(
                connection ->
                        FieldNamedPreparedStatement.prepareStatement(
                                connection, sql, tableSchema.getFieldNames()),
                tableSchema,
                databaseTableSchema,
                rowConverter);
    }

    static String applyOracleAppendValuesHintIfNeeded(
            JdbcSinkConfig jdbcSinkConfig, String insertSQL) {
        if (!isOracleAppendValuesConfigured(jdbcSinkConfig)) {
            return insertSQL;
        }
        final String insertPrefix = "INSERT";
        if (!insertSQL.regionMatches(true, 0, insertPrefix, 0, insertPrefix.length())) {
            throw new IllegalArgumentException(
                    "oracle_insert_mode=APPEND_VALUES only supports generated INSERT statements.");
        }
        String appendValuesSQL =
                insertPrefix + " /*+ APPEND_VALUES */" + insertSQL.substring(insertPrefix.length());
        log.info("Oracle APPEND_VALUES insert mode is enabled, generated SQL: {}", appendValuesSQL);
        return appendValuesSQL;
    }

    private static void validateOracleInsertMode(
            JdbcDialect dialect, JdbcSinkConfig jdbcSinkConfig, List<String> primaryKeys) {
        if (!isOracleAppendValuesConfigured(jdbcSinkConfig)) {
            return;
        }
        if (!DatabaseIdentifier.ORACLE.equals(dialect.dialectName())) {
            throw new IllegalArgumentException(
                    "oracle_insert_mode=APPEND_VALUES only supports Oracle JDBC sink.");
        }
        if (primaryKeys != null && !primaryKeys.isEmpty()) {

View on GitHub (pinned to cf67b549a7)

Solutions

  1. Remove the custom SQL override so the connector generates a standard INSERT statement
  2. Set oracle_insert_mode to the default (INSERT) if the statement is not a plain INSERT
  3. Ensure the dialect is Oracle and the sink is configured for insert-only writes

Example fix

// before
generate_sink_sql = false
sql = "MERGE INTO t USING ..."
// after: let the connector generate INSERT
sql = null
generate_sink_sql = true
Defensive patterns

Strategy: validation

Validate before calling

// config sanity check before build
if (config.get("oracle_insert_mode") == "APPEND_VALUES" && config.get("sql") != null) {
  throw new IllegalArgumentException("APPEND_VALUES requires generated INSERT SQL (no custom sql)");
}

Try / catch

try { builder.build(); } catch (IllegalArgumentException e) {
  if (e.getMessage().contains("APPEND_VALUES")) { /* fall back to default insert mode */ }
}

Prevention

When it happens

Trigger: Setting oracle_insert_mode=APPEND_VALUES while the builder produced a non-INSERT SQL statement (e.g. a custom/override SQL, or a statement generated for upsert semantics).

Common situations: Users copy a configuration using APPEND_VALUES onto a sink whose SQL was customized or auto-generated for MERGE/upsert mode; dialect changed from Oracle but the Oracle-specific option left in config.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/a74442fd367d8206. Report an issue: GitHub.