bytebase/bytebase · error
pseudo constant select: expected 1 statement, got %d
Error message
pseudo constant select: expected 1 statement, got %d
What it means
pseudoConstantSelect expects the synthetic `SELECT ...` string it builds to parse into exactly one statement. Since the string is constructed internally, more than one statement can only mean a target expression contained a statement separator. This error reports the actual statement count.
Source
Thrown at backend/plugin/parser/pg/query_span_loader_pseudo.go:207
// is emitted so the SELECT is well-formed.
func pseudoConstantSelect(columnNames []string) (*ast.SelectStmt, error) {
var targets []string
for _, col := range columnNames {
if col == "" {
continue
}
targets = append(targets, "NULL::text AS "+quoteIdent(col))
}
if len(targets) == 0 {
targets = []string{"NULL::text"}
}
sql := "SELECT " + strings.Join(targets, ", ")
stmts, err := ParsePg(sql)
if err != nil {
return nil, errors.Wrap(err, "parse pseudo constant select")
}
if len(stmts) != 1 {
return nil, errors.Errorf("pseudo constant select: expected 1 statement, got %d", len(stmts))
}
sel, ok := stmts[0].AST.(*ast.SelectStmt)
if !ok {
return nil, errors.Errorf("pseudo constant select: expected SelectStmt, got %T", stmts[0].AST)
}
return sel, nil
}
// functionArgCountFromSignature parses a signature string like
// "my_func(integer, text)" and returns the number of arguments. A signature
// with no parentheses or an empty argument list returns 0.
func functionArgCountFromSignature(signature string) int {
openIdx := strings.Index(signature, "(")
if openIdx < 0 {
return 0
}
closeIdx := strings.LastIndex(signature, ")")
if closeIdx < 0 || closeIdx <= openIdx {View on GitHub (pinned to 1870550677)
Solutions
- Sanitize or reject target expressions containing ';' before building the SQL
- Log the individual targets to find which one embeds a separator
- Re-sync metadata to restore clean column expressions
- Prefer quoting identifiers so special characters are escaped
Example fix
// before
targets := []string{"a; b"} // produces 2 statements
// after
targets := []string{"a", "b"} // "SELECT a, b" Defensive patterns
Strategy: validation
Validate before calling
for _, t := range targets {
if strings.Contains(t, ";") {
return errors.Errorf("target %q contains statement separator", t)
}
} Type guard
func singleStatementTargets(targets []string) bool {
for _, t := range targets { if strings.Contains(t, ";") { return false } }
return true
} Try / catch
sel, err := pseudoConstantSelect(targets)
if err != nil && strings.Contains(err.Error(), "expected 1 statement") {
return nil, fmt.Errorf("sanitize targets %v: %w", targets, err)
} Prevention
- Sanitize semicolons out of metadata-derived expressions
- Split multi-expression strings on ',' before passing as targets
- Assert single-statement output in tests for pseudoConstantSelect
- Re-sync metadata if targets were captured with separators
When it happens
Trigger: A target string passed from pseudoViewStmt/pseudoCreateTableAsStmt contains a semicolon, so "SELECT a; b" parses as two statements instead of one.
Common situations: Unsanitized metadata column expressions containing semicolons; concatenated target lists built incorrectly upstream.
Related errors
- expected 1 statement, got %d
- parse
- parse pseudo constant select
- ssl_cert and ssl_key must be both set or unset
- no valid CERTIFICATE PEM block found
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/bc1d03745a68d3c0.
Report an issue: GitHub.