{"record":{"id":"bc1d03745a68d3c0","repo":"bytebase/bytebase","slug":"pseudo-constant-select-expected-1-statement-got","errorCode":null,"errorMessage":"pseudo constant select: expected 1 statement, got %d","messagePattern":"pseudo constant select: expected 1 statement, got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/plugin/parser/pg/query_span_loader_pseudo.go","lineNumber":207,"sourceCode":"// is emitted so the SELECT is well-formed.\nfunc pseudoConstantSelect(columnNames []string) (*ast.SelectStmt, error) {\n\tvar targets []string\n\tfor _, col := range columnNames {\n\t\tif col == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\ttargets = append(targets, \"NULL::text AS \"+quoteIdent(col))\n\t}\n\tif len(targets) == 0 {\n\t\ttargets = []string{\"NULL::text\"}\n\t}\n\tsql := \"SELECT \" + strings.Join(targets, \", \")\n\tstmts, err := ParsePg(sql)\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"parse pseudo constant select\")\n\t}\n\tif len(stmts) != 1 {\n\t\treturn nil, errors.Errorf(\"pseudo constant select: expected 1 statement, got %d\", len(stmts))\n\t}\n\tsel, ok := stmts[0].AST.(*ast.SelectStmt)\n\tif !ok {\n\t\treturn nil, errors.Errorf(\"pseudo constant select: expected SelectStmt, got %T\", stmts[0].AST)\n\t}\n\treturn sel, nil\n}\n\n// functionArgCountFromSignature parses a signature string like\n// \"my_func(integer, text)\" and returns the number of arguments. A signature\n// with no parentheses or an empty argument list returns 0.\nfunc functionArgCountFromSignature(signature string) int {\n\topenIdx := strings.Index(signature, \"(\")\n\tif openIdx < 0 {\n\t\treturn 0\n\t}\n\tcloseIdx := strings.LastIndex(signature, \")\")\n\tif closeIdx < 0 || closeIdx <= openIdx {","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/parser/pg/query_span_loader_pseudo.go#L189-L225","documentation":"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.","triggerScenarios":"A target string passed from pseudoViewStmt/pseudoCreateTableAsStmt contains a semicolon, so \"SELECT a; b\" parses as two statements instead of one.","commonSituations":"Unsanitized metadata column expressions containing semicolons; concatenated target lists built incorrectly upstream.","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"],"exampleFix":"// before\ntargets := []string{\"a; b\"} // produces 2 statements\n// after\ntargets := []string{\"a\", \"b\"} // \"SELECT a, b\"","handlingStrategy":"validation","validationCode":"for _, t := range targets {\n\tif strings.Contains(t, \";\") {\n\t\treturn errors.Errorf(\"target %q contains statement separator\", t)\n\t}\n}","typeGuard":"func singleStatementTargets(targets []string) bool {\n\tfor _, t := range targets { if strings.Contains(t, \";\") { return false } }\n\treturn true\n}","tryCatchPattern":"sel, err := pseudoConstantSelect(targets)\nif err != nil && strings.Contains(err.Error(), \"expected 1 statement\") {\n\treturn nil, fmt.Errorf(\"sanitize targets %v: %w\", targets, err)\n}","preventionTips":["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"],"tags":["go","postgres","sql-parse","validation"],"backgroundTag":"unexpected-statement-count","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"}