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  }