risingwavelabs/risingwave · error · SinkError::SqlServer

too many column {}

Error message

too many column {}

What it means

SQL Server writes via Tiberius are limited to TIBERIUS_PARAM_MAX parameters per operation; each row consumes params_per_op parameters. If even one row would exceed that limit (so the computed batch size floors to 0), the sink cannot proceed and raises this SqlServer error.

Source

Thrown at src/connector/src/sink/sqlserver.rs:148

    }
}
impl SqlServerSink {
    pub fn new(
        mut config: SqlServerConfig,
        schema: Schema,
        pk_indices: Vec<usize>,
        is_append_only: bool,
    ) -> Result<Self> {
        // Rewrite config because tiberius allows a maximum of 2100 params in one query request.
        const TIBERIUS_PARAM_MAX: usize = 2000;
        let params_per_op = schema.fields().len();
        let tiberius_max_batch_rows = if params_per_op == 0 {
            config.max_batch_rows
        } else {
            ((TIBERIUS_PARAM_MAX as f64 / params_per_op as f64).floor()) as usize
        };
        if tiberius_max_batch_rows == 0 {
            return Err(SinkError::SqlServer(anyhow!(format!(
                "too many column {}",
                params_per_op
            ))));
        }
        config.max_batch_rows = std::cmp::min(config.max_batch_rows, tiberius_max_batch_rows);
        Ok(Self {
            config,
            schema,
            pk_indices,
            is_append_only,
        })
    }
}

impl TryFrom<SinkParam> for SqlServerSink {
    type Error = SinkError;

    fn try_from(param: SinkParam) -> std::result::Result<Self, Self::Error> {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Reduce the number of columns sunk (drop or combine columns, e.g. serialize struct columns to a single string)
  2. Sink into multiple narrower SQL Server tables
  3. Increase TIBERIUS_PARAM_MAX handling by lowering params_per_op (not possible without code change) — or split the sink

Example fix

// before
CREATE SINK s INTO sqlserver ... AS SELECT * FROM wide_mv; -- 700 columns
// after
CREATE SINK s INTO sqlserver ... AS SELECT id, a, b, c FROM wide_mv; -- narrow projection
Defensive patterns

Strategy: validation

Validate before calling

const TIBERIUS_PARAM_MAX: usize = 2100;
fn check_width(num_columns: usize, params_per_column: usize) -> Result<(), String> {
    let params_per_op = num_columns * params_per_column;
    if params_per_op == 0 || params_per_op >= TIBERIUS_PARAM_MAX {
        Err(format!("{} cols * {} params too wide for SQL Server (max {} params)", num_columns, params_per_column, TIBERIUS_PARAM_MAX))
    } else { Ok(()) }
}

Try / catch

match create_sqlserver_sink(cfg) {
    Err(e) if e.to_string().contains("too many column") => split_sink_into_narrower_tables(),
    other => other,
}

Prevention

When it happens

Trigger: Calling SqlServerSink::new (via SinkWriterV1Adapter / sink creation) for a table whose column count is so large that params_per_op >= TIBERIUS_PARAM_MAX, making tiberius_max_batch_rows == 0.

Common situations: Very wide tables (hundreds of columns) being sunk to SQL Server; each column needs multiple params (e.g. INSERT plus upsert MERGE params) blowing past the ~2100 TDS parameter limit.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/cce2f2ae41458b37. Report an issue: GitHub.