github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/parser/show_syntax.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package parser 12 13 import ( 14 "context" 15 "fmt" 16 "strings" 17 18 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgcode" 19 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgerror" 20 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/sem/tree" 21 "github.com/cockroachdb/errors" 22 ) 23 24 // RunShowSyntax analyzes the syntax and reports its structure as data 25 // for the client. Even an error is reported as data. 26 // 27 // Since errors won't propagate to the client as an error, but as 28 // a result, the usual code path to capture and record errors will not 29 // be triggered. Instead, the caller can pass a reportErr closure to 30 // capture errors instead. May be nil. 31 func RunShowSyntax( 32 ctx context.Context, 33 stmt string, 34 report func(ctx context.Context, field, msg string), 35 reportErr func(ctx context.Context, err error), 36 ) { 37 if strings.HasSuffix(stmt, "??") { 38 // A statement (or, more likely, a prefix to a statement) followed 39 // by the help token (??). 40 // 41 // In that case, take a shortcut to avoid the complexity of 42 // instantiating a whole parser just to retrieve a help string. 43 // This also has the benefit of supporting retrieving help for 44 // the special non-terminals e.g. "<source> ??". 45 prefix := strings.ToUpper(strings.TrimSpace(stmt[:len(stmt)-2])) 46 if h, ok := HelpMessages[prefix]; ok { 47 msg := HelpMessage{Command: prefix, HelpMessageBody: h} 48 msgs := msg.String() 49 err := errors.WithHint(pgerror.WithCandidateCode(errors.New(specialHelpErrorPrefix), pgcode.Syntax), msgs) 50 doErr(ctx, report, reportErr, err) 51 return 52 } 53 } 54 55 stmts, err := Parse(stmt) 56 if err != nil { 57 doErr(ctx, report, reportErr, err) 58 } else { 59 for i := range stmts { 60 report(ctx, "sql", tree.AsStringWithFlags(stmts[i].AST, tree.FmtParsable)) 61 } 62 } 63 } 64 65 func doErr( 66 ctx context.Context, 67 report func(ctx context.Context, field, msg string), 68 reportErr func(ctx context.Context, err error), 69 err error, 70 ) { 71 if reportErr != nil { 72 reportErr(ctx, err) 73 } 74 75 pqErr := pgerror.Flatten(err) 76 report(ctx, "error", pqErr.Message) 77 report(ctx, "code", pqErr.Code) 78 if pqErr.Source != nil { 79 if pqErr.Source.File != "" { 80 report(ctx, "file", pqErr.Source.File) 81 } 82 if pqErr.Source.Line > 0 { 83 report(ctx, "line", fmt.Sprintf("%d", pqErr.Source.Line)) 84 } 85 if pqErr.Source.Function != "" { 86 report(ctx, "function", pqErr.Source.Function) 87 } 88 } 89 if pqErr.Detail != "" { 90 report(ctx, "detail", pqErr.Detail) 91 } 92 if pqErr.Hint != "" { 93 report(ctx, "hint", pqErr.Hint) 94 } 95 }