risingwavelabs/risingwave · error

The upstream table name must contain database name prefix…

Error message

The upstream table name must contain database name prefix, e.g. 'database.table'

What it means

For MySQL CDC sources, the upstream table name in the FROM clause must carry a database prefix (`database.table`) because MySQL disallows `.` inside its database and table identifiers, so the name is split on the first `.`. If `external_table_name` has no `.`, `split_once` fails and this error is raised.

Solutions

  1. Write the FROM clause table name as `'database.table'`, e.g. `FROM mysql_src TABLE 'mydb.orders'`.
  2. Alternatively use `schema.table` format if supported for your connector, letting the source's `database.name` be validated against the prefix.
  3. Check the exact name in MySQL (`SHOW TABLES` / `SELECT DATABASE()`) and mirror it in the FROM clause.

Example fix

-- before
CREATE TABLE t FROM mysql_src TABLE 'orders';
-- after
CREATE TABLE t FROM mysql_src TABLE 'mydb.orders';
Defensive patterns

Strategy: validation

Validate before calling

fn valid_mysql_from_name(name: &str) -> bool {
    name.contains('.') && name.split_once('.').map(|(db, t)| !db.is_empty() && !t.is_empty()).unwrap_or(false)
}

Prevention

When it happens

Trigger: `CREATE TABLE t (...) FROM mysql_src TABLE 'mytable'` (or unquoted table name without prefix) where the MySQL CDC source requires `'mydb.mytable'`; same check during `generate_stream_graph_for_replace_table`.

Common situations: Users following Postgres-style `schema.table` examples for MySQL, or omitting the prefix thinking the source's `database.name` suffices.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at src/frontend/src/handler/create_table.rs:1009

/// - For MySQL/Postgres: Returns the original `external_table_name` unchanged.
fn derive_with_options_for_cdc_table(
    source_with_properties: &WithOptionsSecResolved,
    external_table_name: String,
) -> Result<(WithOptionsSecResolved, String)> {
    use source::cdc::{MYSQL_CDC_CONNECTOR, POSTGRES_CDC_CONNECTOR, SQL_SERVER_CDC_CONNECTOR};
    // we should remove the prefix from `full_table_name`
    let source_database_name: &str = source_with_properties
        .get("database.name")
        .ok_or_else(|| anyhow!("The source with properties does not contain 'database.name'"))?
        .as_str();
    let mut with_options = source_with_properties.clone();
    if let Some(connector) = source_with_properties.get(UPSTREAM_SOURCE_KEY) {
        match connector.as_str() {
            MYSQL_CDC_CONNECTOR => {
                // MySQL doesn't allow '.' in database name and table name, so we can split the
                // external table name by '.' to get the table name
                let (db_name, table_name) = external_table_name.split_once('.').ok_or_else(|| {
                    anyhow!("The upstream table name must contain database name prefix, e.g. 'database.table'")
                })?;
                // We allow multiple database names in the source definition
                if !source_database_name
                    .split(',')
                    .map(|s| s.trim())
                    .any(|name| name == db_name)
                {
                    return Err(anyhow!(
                        "The database name `{}` in the FROM clause is not included in the database name `{}` in source definition",
                        db_name,
                        source_database_name
                    ).into());
                }
                with_options.insert(DATABASE_NAME_KEY.into(), db_name.into());
                with_options.insert(TABLE_NAME_KEY.into(), table_name.into());
                // Return original external_table_name unchanged for MySQL
                return Ok((with_options, external_table_name));
            }

View on GitHub (pinned to 6469eb736d)