{"record":{"id":"484b0e40b3d7ebf0","repo":"bytebase/bytebase","slug":"pseudo-constant-select-expected-selectstmt-got-484b0e","errorCode":null,"errorMessage":"pseudo constant select: expected SelectStmt, got %T","messagePattern":"pseudo constant select: expected SelectStmt, got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/schema/pg/walk_through_loader.go","lineNumber":1068,"sourceCode":"\t}\n\tif len(targets) == 0 {\n\t\ttargets = []string{\"NULL::text\"}\n\t}\n\tsql := \"SELECT \" + strings.Join(targets, \", \")\n\tnodes, err := omniparser.Parse(sql)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"parse pseudo constant select\")\n\t}\n\tif nodes == nil || len(nodes.Items) != 1 {\n\t\treturn nil, errors.New(\"pseudo constant select: expected 1 statement\")\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(\"pseudo constant select: expected SelectStmt, got %T\", node)\n\t}\n\treturn sel, nil\n}\n\n// ---- column name helpers ----\n\nfunc wtColNames(tbl *storepb.TableMetadata) []string {\n\tif tbl == nil {\n\t\treturn nil\n\t}\n\tseen := make(map[string]bool, len(tbl.Columns))\n\tout := make([]string, 0, len(tbl.Columns))\n\tfor _, col := range tbl.Columns {\n\t\tif col.Name == \"\" || seen[col.Name] {\n\t\t\tcontinue\n\t\t}\n\t\tseen[col.Name] = true\n\t\tout = append(out, col.Name)","sourceCodeStart":1050,"sourceCodeEnd":1086,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/schema/pg/walk_through_loader.go#L1050-L1086","documentation":"wtPseudoConstantSelect parses a SQL string meant to be a 'pseudo constant' (a plain SELECT) and expects the top-level statement to be a SelectStmt. After unwrapping the RawStmt wrapper, the first statement's concrete AST node type did not match *ast.SelectStmt. This is an internal contract violation: the caller passed SQL whose statement kind is not a simple SELECT, so the loader cannot safely continue walking the AST.","triggerScenarios":"Calling wtPseudoConstantSelect (directly or via wtPseudoViewStmt/wtPseudoMatViewStmt) with SQL that parses to a non-SELECT statement, e.g. an INSERT, a utility statement (SET, VACUUM), a multi-statement string where the first item is not a SELECT, or empty/placeholder text that the parser still returns as some other node type.","commonSituations":"A walkthrough definition file contains a view body or materialized-view definition stored in a non-SELECT form; a typo or edited fixture replaced the SELECT with another statement; a multi-statement SQL blob was passed where only the SELECT body was expected.","solutions":["Inspect the SQL string passed in and ensure the first statement is exactly one plain SELECT statement.","Strip wrapper statements (semicolons, EXPLAIN, extra statements) so only the SELECT body remains.","Log the offending %T value from the error to see which AST node actually arrived and adjust the caller accordingly.","If non-SELECT pseudo constants must be supported, extend wtPseudoConstantSelect to handle that node type instead of erroring."],"exampleFix":"// before\nreturn wtPseudoConstantSelect(ctx, \"CREATE VIEW v AS SELECT 1\")\n// after\nreturn wtPseudoConstantSelect(ctx, \"SELECT 1\")","handlingStrategy":"validation","validationCode":"func isSingleSelect(sql string) bool {\n\ttrimmed := strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(sql), \";\"))\n\treturn strings.HasPrefix(strings.ToLower(trimmed), \"select \") && !strings.Contains(trimmed, \";\")\n}\n// call wtPseudoConstantSelect only if isSingleSelect(sql)","typeGuard":"sel, ok := node.(*ast.SelectStmt)\nif !ok { return fmt.Errorf(\"unexpected node %T\", node) }","tryCatchPattern":"sel, err := wtPseudoConstantSelect(ctx, sql)\nif err != nil {\n\treturn fmt.Errorf(\"pseudo constant %q: %w\", sql, err)\n}","preventionTips":["Only pass bare SELECT bodies (view definitions, not CREATE VIEW statements) to this loader.","Strip trailing semicolons and extra statements before invoking walkthrough loaders.","Log the offending AST node type whenever this family of errors fires."],"tags":["postgresql","parser","ast","internal-invariant"],"backgroundTag":"type-mismatch","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}