bytebase/bytebase · error
expected exactly 1 statement, got %d
Error message
expected exactly 1 statement, got %d
What it means
getOmniQuerySpan parses the SQL text via the omni parser and requires exactly one statement to compute a query span. If the parser returned zero or more than one statement (batch input with multiple statements separated by semicolons, or an unexpected parse result), it cannot determine a single root span and returns this error. Zero-statement and empty-AST cases are handled explicitly before this check.
Source
Thrown at backend/plugin/parser/tsql/query_span_extractor_omni.go:48
// getOmniQuerySpan is the public entry point. Mirrors the ANTLR getQuerySpan flow.
func (q *omniQuerySpanExtractor) getOmniQuerySpan(ctx context.Context, statement string) (*base.QuerySpan, error) {
q.ctx = ctx
q.source = statement
stmts, err := ParseTSQLOmni(statement)
if err != nil {
return nil, err
}
if len(stmts) == 0 {
return &base.QuerySpan{
Type: base.Select,
SourceColumns: make(base.SourceColumnSet),
Results: []base.QuerySpanResult{},
}, nil
}
if len(stmts) != 1 {
return nil, errors.Errorf("expected exactly 1 statement, got %d", len(stmts))
}
root := stmts[0].AST
if root == nil {
// An empty statement. Match ANTLR's zero-AST behavior.
return &base.QuerySpan{
Type: base.Select,
SourceColumns: make(base.SourceColumnSet),
Results: []base.QuerySpanResult{},
}, nil
}
if _, isGo := root.(*ast.GoStmt); isGo {
// A bare "GO" batch separator. ANTLR discards it and returns an empty
// Select span; match that.
return &base.QuerySpan{
Type: base.Select,
SourceColumns: make(base.SourceColumnSet),
Results: []base.QuerySpanResult{},View on GitHub (pinned to 1870550677)
Solutions
- Split the input into individual statements and call the extractor once per statement, aggregating spans yourself.
- Strip non-query prologue statements (SET options, DECLARE) before span extraction, or run extraction only on the SELECT/DML statements.
- If a single statement is expected but the parser returned several, verify there are no stray semicolons or GO batch separators.
Example fix
// before: extract the whole batch
span, err := GetQuerySpan(batchSQL) // "expected exactly 1 statement, got 3"
// after: split and extract per statement
for _, stmt := range splitStatements(batchSQL) {
span, err := GetQuerySpan(stmt)
} Defensive patterns
Strategy: validation
Validate before calling
stmts := splitStatements(sql)
if len(stmts) != 1 {
return fmt.Errorf("span extraction needs a single statement, got %d", len(stmts))
} Try / catch
span, err := getOmniQuerySpan(content)
if err != nil && strings.Contains(err.Error(), "expected exactly 1 statement") {
for _, stmt := range splitStatements(content) {
span, err = getOmniQuerySpan(stmt)
}
} Prevention
- Split batches on semicolons/GO separators and analyze one statement at a time.
- Strip SET/DECLARE prologue statements before requesting a query span.
- Restrict input endpoints (edit-review, lineage) to single-statement SQL.
When it happens
Trigger: Passing a TSQL batch containing multiple semicolon-separated statements to the query span extractor, or any input the omni parser splits into more than one AST root.
Common situations: Users submitting multi-statement scripts (e.g. `SET NOCOUNT ON; SELECT ...;`) for data-care/lineage analysis, or migration scripts pasted in whole instead of per-statement analysis.
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
- failed to parse function definition
- expected exactly 1 statement, got %d
- unsupported query node type %T
- failed to parse statement
- unsupported DML target table source %T
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/deafabca8bae4634.
Report an issue: GitHub.