github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/sem/tree/explain.go (about)

     1  // Copyright 2015 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 tree
    12  
    13  import (
    14  	"strings"
    15  
    16  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgcode"
    17  	"github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgerror"
    18  	"github.com/cockroachdb/cockroachdb-parser/pkg/util"
    19  	"github.com/cockroachdb/cockroachdb-parser/pkg/util/errorutil/unimplemented"
    20  	"github.com/cockroachdb/cockroachdb-parser/pkg/util/pretty"
    21  	"github.com/cockroachdb/errors"
    22  )
    23  
    24  // Explain represents an EXPLAIN statement.
    25  type Explain struct {
    26  	ExplainOptions
    27  
    28  	// Statement is the statement being EXPLAINed.
    29  	Statement Statement
    30  }
    31  
    32  // ExplainAnalyze represents an EXPLAIN ANALYZE statement.
    33  type ExplainAnalyze struct {
    34  	ExplainOptions
    35  
    36  	// Statement is the statement being EXPLAINed.
    37  	Statement Statement
    38  }
    39  
    40  // ExplainOptions contains information about the options passed to an EXPLAIN
    41  // statement.
    42  type ExplainOptions struct {
    43  	Mode  ExplainMode
    44  	Flags [numExplainFlags + 1]bool
    45  }
    46  
    47  // ExplainMode indicates the mode of the explain. The default is ExplainPlan.
    48  type ExplainMode uint8
    49  
    50  const (
    51  	// ExplainPlan shows information about the planNode tree for a query.
    52  	ExplainPlan ExplainMode = 1 + iota
    53  
    54  	// ExplainDistSQL shows the physical distsql plan for a query and whether a
    55  	// query would be run in "auto" DISTSQL mode. See sql/explain_distsql.go for
    56  	// details. If the ANALYZE option is included, the plan is also executed and
    57  	// execution statistics are collected and shown in the diagram.
    58  	ExplainDistSQL
    59  
    60  	// ExplainOpt shows the optimized relational expression (from the cost-based
    61  	// optimizer).
    62  	ExplainOpt
    63  
    64  	// ExplainVec shows the physical vectorized plan for a query and whether a
    65  	// query would be run in "auto" vectorized mode.
    66  	ExplainVec
    67  
    68  	// ExplainDebug generates a statement diagnostics bundle; only used with
    69  	// EXPLAIN ANALYZE.
    70  	ExplainDebug
    71  
    72  	// ExplainDDL generates a DDL plan diagram for the statement.
    73  	ExplainDDL
    74  
    75  	// ExplainGist generates a plan "gist".
    76  	ExplainGist
    77  
    78  	numExplainModes = iota
    79  )
    80  
    81  var explainModeStrings = [...]string{
    82  	ExplainPlan:    "PLAN",
    83  	ExplainDistSQL: "DISTSQL",
    84  	ExplainOpt:     "OPT",
    85  	ExplainVec:     "VEC",
    86  	ExplainDebug:   "DEBUG",
    87  	ExplainDDL:     "DDL",
    88  	ExplainGist:    "GIST",
    89  }
    90  
    91  var explainModeStringMap = func() map[string]ExplainMode {
    92  	m := make(map[string]ExplainMode, numExplainModes)
    93  	for i := ExplainMode(1); i <= numExplainModes; i++ {
    94  		m[explainModeStrings[i]] = i
    95  	}
    96  	return m
    97  }()
    98  
    99  func (m ExplainMode) String() string {
   100  	if m == 0 || m > numExplainModes {
   101  		panic(errors.AssertionFailedf("invalid ExplainMode %d", m))
   102  	}
   103  	return explainModeStrings[m]
   104  }
   105  
   106  // ExplainModes returns a map from EXPLAIN mode strings to ExplainMode.
   107  func ExplainModes() map[string]ExplainMode {
   108  	return explainModeStringMap
   109  }
   110  
   111  // ExplainFlag is a modifier in an EXPLAIN statement (like VERBOSE).
   112  type ExplainFlag uint8
   113  
   114  // Explain flags.
   115  const (
   116  	ExplainFlagVerbose ExplainFlag = 1 + iota
   117  	ExplainFlagTypes
   118  	ExplainFlagEnv
   119  	ExplainFlagCatalog
   120  	ExplainFlagJSON
   121  	ExplainFlagMemo
   122  	ExplainFlagShape
   123  	ExplainFlagViz
   124  	ExplainFlagRedact
   125  	numExplainFlags = iota
   126  )
   127  
   128  var explainFlagStrings = [...]string{
   129  	ExplainFlagVerbose: "VERBOSE",
   130  	ExplainFlagTypes:   "TYPES",
   131  	ExplainFlagEnv:     "ENV",
   132  	ExplainFlagCatalog: "CATALOG",
   133  	ExplainFlagJSON:    "JSON",
   134  	ExplainFlagMemo:    "MEMO",
   135  	ExplainFlagShape:   "SHAPE",
   136  	ExplainFlagViz:     "VIZ",
   137  	ExplainFlagRedact:  "REDACT",
   138  }
   139  
   140  var explainFlagStringMap = func() map[string]ExplainFlag {
   141  	m := make(map[string]ExplainFlag, numExplainFlags)
   142  	for i := ExplainFlag(1); i <= numExplainFlags; i++ {
   143  		m[explainFlagStrings[i]] = i
   144  	}
   145  	return m
   146  }()
   147  
   148  func (f ExplainFlag) String() string {
   149  	if f == 0 || f > numExplainFlags {
   150  		panic(errors.AssertionFailedf("invalid ExplainFlag %d", f))
   151  	}
   152  	return explainFlagStrings[f]
   153  }
   154  
   155  // ExplainFlags returns a map from EXPLAIN flag strings to ExplainFlag.
   156  func ExplainFlags() map[string]ExplainFlag {
   157  	return explainFlagStringMap
   158  }
   159  
   160  // Format implements the NodeFormatter interface.
   161  func (node *Explain) Format(ctx *FmtCtx) {
   162  	ctx.WriteString("EXPLAIN ")
   163  	b := util.MakeStringListBuilder("(", ", ", ") ")
   164  	if node.Mode != ExplainPlan {
   165  		b.Add(ctx, node.Mode.String())
   166  	}
   167  
   168  	for f := ExplainFlag(1); f <= numExplainFlags; f++ {
   169  		if node.Flags[f] {
   170  			b.Add(ctx, f.String())
   171  		}
   172  	}
   173  	b.Finish(ctx)
   174  	ctx.FormatNode(node.Statement)
   175  }
   176  
   177  // doc is part of the docer interface.
   178  func (node *Explain) doc(p *PrettyCfg) pretty.Doc {
   179  	d := pretty.Keyword("EXPLAIN")
   180  	var opts []pretty.Doc
   181  	if node.Mode != ExplainPlan {
   182  		opts = append(opts, pretty.Keyword(node.Mode.String()))
   183  	}
   184  	for f := ExplainFlag(1); f <= numExplainFlags; f++ {
   185  		if node.Flags[f] {
   186  			opts = append(opts, pretty.Keyword(f.String()))
   187  		}
   188  	}
   189  	if len(opts) > 0 {
   190  		d = pretty.ConcatSpace(
   191  			d,
   192  			p.bracket("(", p.commaSeparated(opts...), ")"),
   193  		)
   194  	}
   195  	return p.nestUnder(d, p.Doc(node.Statement))
   196  }
   197  
   198  // Format implements the NodeFormatter interface.
   199  func (node *ExplainAnalyze) Format(ctx *FmtCtx) {
   200  	ctx.WriteString("EXPLAIN ANALYZE ")
   201  	b := util.MakeStringListBuilder("(", ", ", ") ")
   202  	if node.Mode != ExplainPlan {
   203  		b.Add(ctx, node.Mode.String())
   204  	}
   205  
   206  	for f := ExplainFlag(1); f <= numExplainFlags; f++ {
   207  		if node.Flags[f] {
   208  			b.Add(ctx, f.String())
   209  		}
   210  	}
   211  	b.Finish(ctx)
   212  	ctx.FormatNode(node.Statement)
   213  }
   214  
   215  // doc is part of the docer interface.
   216  func (node *ExplainAnalyze) doc(p *PrettyCfg) pretty.Doc {
   217  	d := pretty.Keyword("EXPLAIN ANALYZE")
   218  	var opts []pretty.Doc
   219  	if node.Mode != ExplainPlan {
   220  		opts = append(opts, pretty.Keyword(node.Mode.String()))
   221  	}
   222  	for f := ExplainFlag(1); f <= numExplainFlags; f++ {
   223  		if node.Flags[f] {
   224  			opts = append(opts, pretty.Keyword(f.String()))
   225  		}
   226  	}
   227  	if len(opts) > 0 {
   228  		d = pretty.ConcatSpace(
   229  			d,
   230  			p.bracket("(", p.commaSeparated(opts...), ")"),
   231  		)
   232  	}
   233  	return p.nestUnder(d, p.Doc(node.Statement))
   234  }
   235  
   236  // MakeExplain parses the EXPLAIN option strings and generates an Explain
   237  // or ExplainAnalyze statement.
   238  func MakeExplain(options []string, stmt Statement) (Statement, error) {
   239  	for i := range options {
   240  		options[i] = strings.ToUpper(options[i])
   241  	}
   242  	var opts ExplainOptions
   243  	var analyze bool
   244  	for _, opt := range options {
   245  		opt = strings.ToUpper(opt)
   246  		if m, ok := explainModeStringMap[opt]; ok {
   247  			if opts.Mode != 0 {
   248  				return nil, pgerror.Newf(pgcode.Syntax, "cannot set EXPLAIN mode more than once: %s", opt)
   249  			}
   250  			opts.Mode = m
   251  			continue
   252  		}
   253  		if opt == "ANALYZE" {
   254  			analyze = true
   255  			continue
   256  		}
   257  		flag, ok := explainFlagStringMap[opt]
   258  		if !ok {
   259  			return nil, pgerror.Newf(pgcode.Syntax, "unsupported EXPLAIN option: %s", opt)
   260  		}
   261  		opts.Flags[flag] = true
   262  	}
   263  	if opts.Mode == 0 {
   264  		// Default mode is ExplainPlan.
   265  		opts.Mode = ExplainPlan
   266  	}
   267  	if opts.Flags[ExplainFlagJSON] {
   268  		if opts.Mode != ExplainDistSQL {
   269  			return nil, pgerror.Newf(pgcode.Syntax, "the JSON flag can only be used with DISTSQL")
   270  		}
   271  		if analyze {
   272  			return nil, pgerror.Newf(pgcode.Syntax, "the JSON flag cannot be used with ANALYZE")
   273  		}
   274  	}
   275  
   276  	if opts.Flags[ExplainFlagEnv] {
   277  		if opts.Mode != ExplainOpt {
   278  			return nil, pgerror.Newf(pgcode.Syntax, "the ENV flag can only be used with OPT")
   279  		}
   280  	}
   281  
   282  	if opts.Flags[ExplainFlagRedact] {
   283  		// TODO(michae2): Support redaction of other EXPLAIN modes.
   284  		switch opts.Mode {
   285  		case ExplainPlan:
   286  		case ExplainOpt:
   287  		case ExplainVec:
   288  		case ExplainDebug:
   289  		default:
   290  			return nil, unimplemented.Newf(
   291  				"EXPLAIN (REDACT)", "the REDACT flag cannot be used with %s", opts.Mode,
   292  			)
   293  		}
   294  
   295  		if opts.Flags[ExplainFlagEnv] {
   296  			return nil, unimplemented.Newf(
   297  				"EXPLAIN (REDACT)", "the REDACT flag cannot be used with %s, ENV", opts.Mode,
   298  			)
   299  		}
   300  	}
   301  
   302  	if analyze {
   303  		if opts.Mode != ExplainDistSQL && opts.Mode != ExplainDebug && opts.Mode != ExplainPlan {
   304  			return nil, pgerror.Newf(pgcode.Syntax, "EXPLAIN ANALYZE cannot be used with %s", opts.Mode)
   305  		}
   306  		return &ExplainAnalyze{
   307  			ExplainOptions: opts,
   308  			Statement:      stmt,
   309  		}, nil
   310  	}
   311  
   312  	if opts.Mode == ExplainDebug {
   313  		return nil, pgerror.Newf(pgcode.Syntax, "DEBUG flag can only be used with EXPLAIN ANALYZE")
   314  	}
   315  	return &Explain{
   316  		ExplainOptions: opts,
   317  		Statement:      stmt,
   318  	}, nil
   319  }