{"record":{"id":"9593b2accebf7c9d","repo":"bytebase/bytebase","slug":"default-q-expected-selectstmt","errorCode":null,"errorMessage":"default %q: expected SelectStmt","messagePattern":"default %q: expected SelectStmt","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/schema/pg/walk_through_loader.go","lineNumber":1184,"sourceCode":"\treturn cast.TypeName, nil\n}\n\n// wtParseDefaultExpr parses a default expression string and returns the AST node.\nfunc wtParseDefaultExpr(expr string) (ast.Node, error) {\n\tnodes, err := omniparser.Parse(\"SELECT \" + expr)\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"parse default %q\", expr)\n\t}\n\tif nodes == nil || len(nodes.Items) != 1 {\n\t\treturn nil, errors.Errorf(\"default %q: expected 1 statement\", expr)\n\t}\n\tnode := nodes.Items[0]\n\tif raw, ok := node.(*ast.RawStmt); ok {\n\t\tnode = raw.Stmt\n\t}\n\tsel, ok := node.(*ast.SelectStmt)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"default %q: expected SelectStmt\", expr)\n\t}\n\tif sel.TargetList == nil || len(sel.TargetList.Items) != 1 {\n\t\treturn nil, errors.Errorf(\"default %q: expected 1 target\", expr)\n\t}\n\trt, ok := sel.TargetList.Items[0].(*ast.ResTarget)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"default %q: expected ResTarget\", expr)\n\t}\n\treturn rt.Val, nil\n}\n\n// wtParseSelectBody parses a SQL string into *ast.SelectStmt.\nfunc wtParseSelectBody(sql string) (*ast.SelectStmt, error) {\n\tnodes, err := omniparser.Parse(sql)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"parse\")\n\t}\n\tif nodes == nil || len(nodes.Items) != 1 {","sourceCodeStart":1166,"sourceCodeEnd":1202,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/schema/pg/walk_through_loader.go#L1166-L1202","documentation":"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.","triggerScenarios":"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.","commonSituations":"Fixture defaults accidentally containing 'SELECT ...' or other statements; expressions copied with leading comments or keywords; omni parser upgrades changing how constructed queries are classified.","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."],"exampleFix":"// before\nwtParseDefaultExpr(\"SELECT now()\")\n// after\nwtParseDefaultExpr(\"now()\")","handlingStrategy":"validation","validationCode":"stmtKeywords := []string{\"select\", \"insert\", \"update\", \"delete\", \"with\", \"set\"}\nfirst := strings.Fields(strings.ToLower(expr))\nif len(first) > 0 && slices.Contains(stmtKeywords, first[0]) {\n\treturn errors.New(\"default must be a bare expression, not a statement\")\n}","typeGuard":"sel, ok := node.(*ast.SelectStmt)\nif !ok { return fmt.Errorf(\"default %q parsed as %T\", expr, node) }","tryCatchPattern":"node, err := wtParseDefaultExpr(expr)\nif err != nil {\n\treturn fmt.Errorf(\"default %q not an expression: %w\", expr, err)\n}","preventionTips":["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."],"tags":["postgresql","parser","ast","default-expression"],"backgroundTag":"type-mismatch","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}