risingwavelabs/risingwave · error

column "{}" cannot be altered; only types containing struct

Error message

column "{}" cannot be altered; only types containing struct can be altered

What it means

`ColIdGenerator::generate` rejects altering a column whose data type returns `None` from `can_alter()`: only types containing struct are supported for in-place type alteration. Altering e.g. an INT to VARCHAR (or any non-struct-containing change) is not implemented and is explicitly rejected.

Source

Thrown at src/frontend/src/handler/create_table/col_id_gen.rs:173

    pub fn generate(&mut self, col: &mut ColumnCatalog) -> Result<()> {
        let mut path = vec![Segment::Field(col.name().to_owned())];

        if let Some((original_column_id, original_data_type)) = self.existing.get(&path) {
            if original_data_type == col.data_type() {
                col.column_desc.column_id = *original_column_id;
                // Equality above ignores nested field IDs. We need to clone them below.
                col.column_desc.data_type = original_data_type.clone();
                return Ok(());
            } else {
                // Check if the column can be altered.
                match original_data_type.can_alter() {
                    Some(true) => { /* pass */ }
                    Some(false) => bail!(
                        "column \"{}\" was persisted with legacy encoding thus cannot be altered, \
                         consider dropping and readding the column",
                        col.name()
                    ),
                    None => bail!(
                        "column \"{}\" cannot be altered; only types containing struct can be altered",
                        col.name()
                    ),
                }
            }
        }

        fn handle(
            this: &mut ColumnIdGenerator,
            path: &mut Path,
            data_type: DataType,
        ) -> Result<(ColumnId, DataType)> {
            macro_rules! with_segment {
                ($segment:expr, $block:block) => {{
                    path.push($segment);
                    let ret = $block;
                    path.pop();
                    ret

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Drop and re-add the column with the new type, backfilling data explicitly (with casts) if needed.
  2. Create a new column, copy casted values, drop the old one, then rename.
  3. Check RisingWave docs for the supported ALTER COLUMN TYPE matrix (struct-containing types only).

Example fix

-- before (unsupported)
ALTER TABLE t ALTER COLUMN qty TYPE bigint;
-- after
ALTER TABLE t ADD COLUMN qty_big bigint;
UPDATE t SET qty_big = qty::bigint;
ALTER TABLE t DROP COLUMN qty;
ALTER TABLE t RENAME COLUMN qty_big TO qty;
Defensive patterns

Strategy: validation

Validate before calling

if original.can_alter().is_none() {
    // plan drop+readd instead of ALTER COLUMN TYPE
    return Err(format!("column '{}' type change not supported; use drop+readd", col.name()));
}

Prevention

When it happens

Trigger: `ALTER TABLE ... ALTER COLUMN c TYPE <new_type>` where the original or new type contains no struct — e.g. changing an `int` column to `varchar`, or `varchar` to `int`.

Common situations: Users expecting general type-change support like in OLTP databases; migration scripts generated for Postgres applied to RisingWave.

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 risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/862f4d3591919b71. Report an issue: GitHub.