risingwavelabs/risingwave · error · Internal

eval_row in iceberg_transform is not supported yet

Error message

eval_row in iceberg_transform is not supported yet

What it means

The iceberg_transform expression only implements row-batch (array) evaluation; its eval_row path is deliberately unimplemented and always returns this internal error. Row-at-a-time evaluation of Iceberg partition transforms is not supported.

Solutions

  1. Evaluate the expression via the array/batch path (eval) instead of eval_row.
  2. If you must evaluate row-wise, wrap the row in a single-row array and call eval.
  3. If this is a legitimate need, file/await upstream support for eval_row in iceberg_transform.

Example fix

// before
let datum = expr.eval_row(&row)?;
// after
let array = expr.eval(&single_row_array)?;
let datum = array.value_at(0).to_owned_datum();
Defensive patterns

Strategy: fallback

Validate before calling

// Avoid row-wise evaluation of iceberg_transform; use batch eval:
// let array = expr.eval(&input_array)?;

Try / catch

match expr.eval_row(row) {
    Ok(d) => d,
    Err(e) if e.to_string().contains("not supported") => {
        // fall back to batch evaluation
        let arr = expr.eval(&single_row_array)?;
        arr.value_at(0).to_owned_datum()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Evaluating an iceberg_transform expression through the sync/row evaluation path — e.g. code that calls ExprVisitor::eval_row or evaluates the expression against a single OwnedRow instead of an array.

Common situations: New executor or tooling code path that evaluates expressions per-row rather than per-batch; using iceberg_transform in a context (e.g. some UDF wrappers or row-based joins) that requires eval_row.

Related errors


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

Appendix: source

Thrown at src/expr/impl/src/scalar/external/iceberg.rs:73

    }
}

impl SyncExpression for IcebergTransform {
    fn eval(&self, data_chunk: &DataChunk) -> Result<ArrayRef> {
        let array = self.child.eval(data_chunk)?;
        // Convert to arrow array
        let arrow_array = IcebergArrowConvert.to_arrow_array(&self.input_arrow_type, &array)?;
        // Transform
        let res_array = self.transform.transform(arrow_array).unwrap();
        // Convert back to array ref and return it
        Ok(Arc::new(IcebergArrowConvert.array_from_arrow_array(
            &self.output_arrow_field,
            &res_array,
        )?))
    }

    fn eval_row(&self, _row: &OwnedRow) -> Result<Datum> {
        Err(ExprError::Internal(anyhow!(
            "eval_row in iceberg_transform is not supported yet"
        )))
    }
}

#[build_function("iceberg_transform(varchar, any) -> any", type_infer = "unreachable")]
fn build(return_type: DataType, mut children: Vec<BoxedExpression>) -> Result<BoxedExpression> {
    let transform_type = {
        let datum = children[0].eval_const()?.unwrap();
        let str = datum.as_utf8();
        Transform::from_str(str).map_err(|_| ExprError::InvalidParam {
            name: "transform type in iceberg_transform",
            reason: format!("Fail to parse {str} as iceberg transform type").into(),
        })?
    };

    // For Identity and Void transform, we will use `InputRef` and const null in frontend,
    // so it should not reach here.

View on GitHub (pinned to 6469eb736d)