risingwavelabs/risingwave · error · UnconsumedShiftRight

unconsumed `>>`

Error message

unconsumed `>>`

What it means

Raised by the SQL parser's `data_type` entry point when, after parsing a data type, a stray `>>` (shift-right) token remains unconsumed. This happens when a user writes nested generic type parameters like `array<array<int>>` where the parser cannot merge the two `>` into valid closing brackets. It is a syntax-level sanity check at the end of type parsing.

Solutions

  1. Insert a space between consecutive closing brackets: `ARRAY<ARRAY<INT> >` instead of `ARRAY<ARRAY<INT>>`.
  2. Rewrite the type using a non-generic equivalent if the dialect supports it (e.g. nested struct helpers).
  3. If you maintain the parser, split the `>>` token into two `>` tokens in the tokenizer or handle bracket merging in `data_type_verify_state`.

Example fix

// before
CREATE TABLE t (a STRUCT<v ARRAY<INT>>);
// after
CREATE TABLE t (a STRUCT<v ARRAY<INT> >);
Defensive patterns

Strategy: validation

Validate before calling

fn has_double_gt_at_type_end(sql_type: &str) -> bool {
    sql_type.trim_end().ends_with(">")
        && sql_type.contains(">>")
}
// if has_double_gt_at_type_end("ARRAY<ARRAY<INT>>") { insert space between brackets }

Prevention

When it happens

Trigger: Calling `data_type()` on input whose type expression ends with consecutive closing brackets forming a `>>` token, e.g. `STRUCT<a ARRAY<INT>>` or `MAP<INT, ARRAY<VARCHAR>>` in a DDL/CAST context.

Common situations: Users writing deeply nested types copied from other SQL dialects (Postgres/Hive/Spark) that allow `>>` in type syntax; hand-written CREATE TABLE with nested collections; tooling that generates types programmatically without spacing.

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/24c623f5b53c0ee3. Report an issue: GitHub.

Appendix: source

Thrown at src/sqlparser/src/parser_v2/data_type.rs:132

    )
    .context(StrContext::Label("struct_data_type"))
    .parse_next(input)
}

/// Consume a data type definition.
///
/// The parser is the main entry point for data type parsing.
///
/// Note: in recursion, we should use `data_type_stateful` instead of `data_type`,
/// otherwise the type parameter will recurse like `Stateful<Stateful<Stateful<...>>>`.
/// Also note that we cannot use `Parser<'_>` directly to avoid misuse, because we need
/// generics `<S>` to parameterize over `Parser<'_>` and `Stateful<Parser<'_>>`.
pub fn data_type<S>(input: &mut S) -> ModalResult<DataType>
where
    S: TokenStream,
{
    #[derive(Debug, thiserror::Error)]
    #[error("unconsumed `>>`")]
    struct UnconsumedShiftRight;

    with_state::<S, DataTypeParsingState, _, _, _>(terminated(
        data_type_stateful,
        trace("data_type_verify_state", |input: &mut StatefulStream<S>| {
            // If there is remaining `>`, we should fail.
            if *input.state.remaining_close.borrow() > 0 {
                Err(ErrMode::Cut(ContextError::from_external_error(
                    input,
                    UnconsumedShiftRight,
                )))
            } else {
                Ok(())
            }
        }),
    ))
    .context(StrContext::Label("data_type"))
    .parse_next(input)

View on GitHub (pinned to 6469eb736d)