risingwavelabs/risingwave · error

expect `CREATE TABLE` or `CREATE SOURCE` statement, found: `

Error message

expect `CREATE TABLE` or `CREATE SOURCE` statement, found: `{base:?}`

What it means

In `try_purify_table_source_create_sql_ast`, RisingWave purifies a parsed CREATE statement (removing wildcards and connector-injected columns) to rebuild table/source definitions. The let-else only accepts `CREATE TABLE` or `CREATE SOURCE` ASTs; anything else bails with this message including the debug repr of the statement.

Source

Thrown at src/frontend/src/catalog/purify.rs:86

    let (Statement::CreateTable {
        columns: column_defs,
        constraints,
        wildcard_idx,
        include_column_options,
        ..
    }
    | Statement::CreateSource {
        stmt:
            CreateSourceStatement {
                columns: column_defs,
                constraints,
                wildcard_idx,
                include_column_options,
                ..
            },
    }) = &mut base
    else {
        bail!("expect `CREATE TABLE` or `CREATE SOURCE` statement, found: `{base:?}`");
    };

    // First, remove the wildcard from the definition.
    *wildcard_idx = None;

    // Filter out columns that are not defined by users in SQL.
    let defined_columns = columns.iter().filter(|c| c.is_defined_in_columns_clause());

    // Derive `ColumnDef` from `ColumnCatalog`.
    let mut purified_column_defs = Vec::new();
    for column in defined_columns {
        let mut column_def = if let Some(existing) = column_defs
            .iter()
            .find(|c| c.name.real_value() == column.name())
        {
            // If the column is already defined in the persisted definition, retrieve it.
            existing.clone()
        } else {

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Ensure the SQL string passed to purification is a `CREATE TABLE` or `CREATE SOURCE` statement.
  2. Check what produced the persisted/derived SQL and why its statement kind changed (e.g. version migration or manual edit of persisted definition).
  3. Guard the call site: only invoke purification for table/source creation paths.

Example fix

// before
let ast = parse(sql);
let purified = try_purify_table_source_create_sql_ast(ast)?; // sql was CREATE MATERIALIZED VIEW
// after
assert!(sql.trim_start().to_uppercase().starts_with("CREATE TABLE") || sql.trim_start().to_uppercase().starts_with("CREATE SOURCE"));
let purified = try_purify_table_source_create_sql_ast(parse(sql))?;
Defensive patterns

Strategy: validation

Validate before calling

const upper = sql.trimStart().toUpperCase();
if (!upper.startsWith("CREATE TABLE") && !upper.startsWith("CREATE SOURCE")) {
  throw new Error("purification requires CREATE TABLE or CREATE SOURCE");
}

Type guard

function isPurifiableStatement(ast) {
  return ast && (ast.statement.CreateTable !== undefined || ast.statement.CreateSource !== undefined);
}

Try / catch

match try_purify_table_source_create_sql_ast(ast) {
    Err(e) if e.to_string().contains("expect `CREATE TABLE` or `CREATE SOURCE`") => {
        // log the statement kind and skip purification
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling `try_purify_table_source_create_sql_ast` (directly or via `create_sql_ast_purified` / `get_new_table_definition_for_cdc_table`) with a parsed statement that is not CREATE TABLE/CREATE SOURCE, e.g. CREATE MATERIALIZED VIEW, CREATE SINK, or an ALTER statement.

Common situations: Internal tooling or CDC table-definition re-resolution fed the wrong statement kind; schema-change flows storing/reading a persisted SQL string that was later changed to a non-TABLE/SOURCE statement.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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