bytebase/bytebase · error
default %q: expected SelectStmt
Error message
default %q: expected SelectStmt
What it means
After unwrapping RawStmt, wtParseDefaultExpr asserts the single statement is *ast.SelectStmt, since it built 'SELECT <expr>'. If the parser classified the input as another statement kind, this error fires. Unlike sibling checks it omits the %T detail, so the actual node type must be inspected at the call site.
Source
Thrown at backend/plugin/schema/pg/walk_through_loader.go:1184
return cast.TypeName, nil
}
// wtParseDefaultExpr parses a default expression string and returns the AST node.
func wtParseDefaultExpr(expr string) (ast.Node, error) {
nodes, err := omniparser.Parse("SELECT " + expr)
if err != nil {
return nil, errors.Wrapf(err, "parse default %q", expr)
}
if nodes == nil || len(nodes.Items) != 1 {
return nil, errors.Errorf("default %q: expected 1 statement", expr)
}
node := nodes.Items[0]
if raw, ok := node.(*ast.RawStmt); ok {
node = raw.Stmt
}
sel, ok := node.(*ast.SelectStmt)
if !ok {
return nil, errors.Errorf("default %q: expected SelectStmt", expr)
}
if sel.TargetList == nil || len(sel.TargetList.Items) != 1 {
return nil, errors.Errorf("default %q: expected 1 target", expr)
}
rt, ok := sel.TargetList.Items[0].(*ast.ResTarget)
if !ok {
return nil, errors.Errorf("default %q: expected ResTarget", expr)
}
return rt.Val, nil
}
// wtParseSelectBody parses a SQL string into *ast.SelectStmt.
func wtParseSelectBody(sql string) (*ast.SelectStmt, error) {
nodes, err := omniparser.Parse(sql)
if err != nil {
return nil, errors.Wrap(err, "parse")
}
if nodes == nil || len(nodes.Items) != 1 {View on GitHub (pinned to 1870550677)
Solutions
- Ensure the default value is a bare expression, not a full SQL statement.
- Remove any leading keywords/comments from the expression string.
- Temporarily log the parsed node type (or add %T to the message) to identify the unexpected shape.
- Check omni parser version changes if previously valid defaults now fail.
Example fix
// before
wtParseDefaultExpr("SELECT now()")
// after
wtParseDefaultExpr("now()") Defensive patterns
Strategy: validation
Validate before calling
stmtKeywords := []string{"select", "insert", "update", "delete", "with", "set"}
first := strings.Fields(strings.ToLower(expr))
if len(first) > 0 && slices.Contains(stmtKeywords, first[0]) {
return errors.New("default must be a bare expression, not a statement")
} Type guard
sel, ok := node.(*ast.SelectStmt)
if !ok { return fmt.Errorf("default %q parsed as %T", expr, node) } Try / catch
node, err := wtParseDefaultExpr(expr)
if err != nil {
return fmt.Errorf("default %q not an expression: %w", expr, err)
} Prevention
- Store defaults as bare expressions, never full statements.
- Strip leading statement keywords and comments from expression fields.
- Include %T in the error message to speed diagnosis of node-shape surprises.
When it happens
Trigger: A default expression whose text begins with a non-expression keyword (e.g. 'SELECT', 'INSERT', 'SET') so 'SELECT '+expr parses to a nested or different statement; parser-version-specific classification of the constructed SQL.
Common situations: Fixture defaults accidentally containing 'SELECT ...' or other statements; expressions copied with leading comments or keywords; omni parser upgrades changing how constructed queries are classified.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- pseudo constant select: expected SelectStmt, got %T
- type %q: expected SelectStmt, got %T
- type %q: expected ResTarget, got %T
- type %q: expected TypeCast, got %T
- parse default %q
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/9593b2accebf7c9d.
Report an issue: GitHub.