risingwavelabs/risingwave · error

auto schema change with drop column only supports Project wi

Error message

auto schema change with drop column only supports Project with InputRef

What it means

During ALTER TABLE auto schema change, the meta service rewrites the sink fragment's Project node to remap InputRefs after columns are dropped. If the Project's select list contains any expression that is not a bare InputRef (e.g. a function call or literal) AND columns were removed, the rewrite cannot remap it safely, so `rewrite_project_node` fails fast. The library only supports pure column-pass-through Projects when dropping columns.

Source

Thrown at src/meta/src/stream/stream_graph/fragment.rs:696

    project_node: &mut StreamNode,
    scan_rewrite: &ScanRewriteResult,
    newly_added_columns: &[ColumnCatalog],
    removed_column_ids: &HashSet<ColumnId>,
    upstream_table_name: &str,
) -> MetaResult<()> {
    let PbNodeBody::Project(project_node_body) = project_node.node_body.as_mut().unwrap() else {
        return Err(anyhow!(
            "expect PbNodeBody::Project but got: {:?}",
            project_node.node_body
        )
        .into());
    };
    let has_non_input_ref = project_node_body
        .select_list
        .iter()
        .any(|expr| !matches!(expr.rex_node, Some(expr_node::RexNode::InputRef(_))));
    if has_non_input_ref && !removed_column_ids.is_empty() {
        return Err(anyhow!(
            "auto schema change with drop column only supports Project with InputRef"
        )
        .into());
    }

    let mut new_select_list = Vec::with_capacity(project_node_body.select_list.len());
    let mut new_project_fields = Vec::with_capacity(project_node.fields.len());
    for (index, expr) in project_node_body.select_list.iter().enumerate() {
        let mut new_expr = expr.clone();
        if let Some(expr_node::RexNode::InputRef(old_index)) = new_expr.rex_node {
            let Some(&new_index) = scan_rewrite
                .old_output_index_to_new_output_index
                .get(&old_index)
            else {
                continue;
            };
            new_expr.rex_node = Some(expr_node::RexNode::InputRef(new_index));
        } else if !removed_column_ids.is_empty() {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Rewrite the MV so the Project is a pure column pass-through before dropping columns, computing expressions elsewhere.
  2. Drop the column without computed expressions in the pipeline: recreate the MV without the column instead of ALTER.
  3. If you maintain RisingWave, extend rewrite_project_node to remap InputRef children inside non-InputRef expressions.

Example fix

-- before
CREATE MV mv AS SELECT a, a + b AS s FROM t;
ALTER TABLE t DROP COLUMN b; -- fails: Project contains a+b
-- after
CREATE MV mv AS SELECT a, s FROM (SELECT a, a + b AS s FROM t) sub; -- or recreate MV without b
Defensive patterns

Strategy: validation

Validate before calling

// Before ALTER ... DROP COLUMN, ensure the MV select list is pure pass-through
// e.g. check the MV definition contains only plain column references over the altered table.

Prevention

When it happens

Trigger: Calling `rewrite_refresh_schema_sink_fragment` (via replace_job for ALTER TABLE DROP COLUMN) where the MV/sink pipeline between the StreamScan and the Sink is a Project containing computed expressions (functions, literals, casts) rather than only InputRefs, with a non-empty removed_column_ids set.

Common situations: User alters a materialized view to drop a column while the MV's select list computes derived expressions (e.g. `SELECT a + b AS c`), so the sink input is a Project with non-InputRef expressions.

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/8bc0de7f90f8c7e4. Report an issue: GitHub.