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
- Ensure the SQL string passed to purification is a `CREATE TABLE` or `CREATE SOURCE` statement.
- Check what produced the persisted/derived SQL and why its statement kind changed (e.g. version migration or manual edit of persisted definition).
- 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
- Only pass CREATE TABLE / CREATE SOURCE SQL into purification helpers
- Validate persisted table-definition SQL after migrations or manual edits
- Add an AST-kind assertion at call sites that produce the SQL
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
- {object_type} not found: {name}
- {object_type} named {name} already exists{}
- Catalog error: {0}
- catalog error: {0}
- object {} not found
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/3bcec6d12805c977.
Report an issue: GitHub.