github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/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  	"fmt"
    15  	"strings"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    19  	"github.com/cockroachdb/errors"
    20  )
    21  
    22  // Explain represents an EXPLAIN statement.
    23  type Explain struct {
    24  	ExplainOptions
    25  
    26  	// Statement is the statement being EXPLAINed.
    27  	Statement Statement
    28  }
    29  
    30  // ExplainOptions contains information about the options passed to an EXPLAIN
    31  // statement.
    32  type ExplainOptions struct {
    33  	Mode  ExplainMode
    34  	Flags [numExplainFlags + 1]bool
    35  }
    36  
    37  // ExplainMode indicates the mode of the explain. Currently there are two modes:
    38  // PLAN (the default) and DISTSQL.
    39  type ExplainMode uint8
    40  
    41  const (
    42  	// ExplainPlan shows information about the planNode tree for a query.
    43  	ExplainPlan ExplainMode = 1 + iota
    44  
    45  	// ExplainDistSQL shows the physical distsql plan for a query and whether a
    46  	// query would be run in "auto" DISTSQL mode. See sql/explain_distsql.go for
    47  	// details. If the ANALYZE option is included, the plan is also executed and
    48  	// execution statistics are collected and shown in the diagram.
    49  	ExplainDistSQL
    50  
    51  	// ExplainOpt shows the optimized relational expression (from the cost-based
    52  	// optimizer).
    53  	ExplainOpt
    54  
    55  	// ExplainVec shows the physical vectorized plan for a query and whether a
    56  	// query would be run in "auto" vectorized mode.
    57  	ExplainVec
    58  
    59  	numExplainModes = iota
    60  )
    61  
    62  var explainModeStrings = [...]string{
    63  	ExplainPlan:    "PLAN",
    64  	ExplainDistSQL: "DISTSQL",
    65  	ExplainOpt:     "OPT",
    66  	ExplainVec:     "VEC",
    67  }
    68  
    69  var explainModeStringMap = func() map[string]ExplainMode {
    70  	m := make(map[string]ExplainMode, numExplainModes)
    71  	for i := ExplainMode(1); i <= numExplainModes; i++ {
    72  		m[explainModeStrings[i]] = i
    73  	}
    74  	return m
    75  }()
    76  
    77  func (m ExplainMode) String() string {
    78  	if m == 0 || m > numExplainModes {
    79  		panic(errors.AssertionFailedf("invalid ExplainMode %d", m))
    80  	}
    81  	return explainModeStrings[m]
    82  }
    83  
    84  // ExplainFlag is a modifier in an EXPLAIN statement (like VERBOSE).
    85  type ExplainFlag uint8
    86  
    87  // Explain flags.
    88  const (
    89  	ExplainFlagVerbose ExplainFlag = 1 + iota
    90  	ExplainFlagSymVars
    91  	ExplainFlagTypes
    92  	ExplainFlagNoNormalize
    93  	ExplainFlagAnalyze
    94  	ExplainFlagEnv
    95  	ExplainFlagCatalog
    96  	numExplainFlags = iota
    97  )
    98  
    99  var explainFlagStrings = [...]string{
   100  	ExplainFlagVerbose:     "VERBOSE",
   101  	ExplainFlagSymVars:     "SYMVARS",
   102  	ExplainFlagTypes:       "TYPES",
   103  	ExplainFlagNoNormalize: "NONORMALIZE",
   104  	ExplainFlagAnalyze:     "ANALYZE",
   105  	ExplainFlagEnv:         "ENV",
   106  	ExplainFlagCatalog:     "CATALOG",
   107  }
   108  
   109  var explainFlagStringMap = func() map[string]ExplainFlag {
   110  	m := make(map[string]ExplainFlag, numExplainFlags)
   111  	for i := ExplainFlag(1); i <= numExplainFlags; i++ {
   112  		m[explainFlagStrings[i]] = i
   113  	}
   114  	return m
   115  }()
   116  
   117  func (f ExplainFlag) String() string {
   118  	if f == 0 || f > numExplainFlags {
   119  		panic(errors.AssertionFailedf("invalid ExplainFlag %d", f))
   120  	}
   121  	return explainFlagStrings[f]
   122  }
   123  
   124  // Format implements the NodeFormatter interface.
   125  func (node *Explain) Format(ctx *FmtCtx) {
   126  	ctx.WriteString("EXPLAIN ")
   127  	// ANALYZE is a special case because it is a statement implemented as an
   128  	// option to EXPLAIN.
   129  	if node.Flags[ExplainFlagAnalyze] {
   130  		ctx.WriteString("ANALYZE ")
   131  	}
   132  	wroteFlag := false
   133  	if node.Mode != ExplainPlan {
   134  		fmt.Fprintf(ctx, "(%s", node.Mode)
   135  		wroteFlag = true
   136  	}
   137  
   138  	for f := ExplainFlag(1); f <= numExplainFlags; f++ {
   139  		if f != ExplainFlagAnalyze && node.Flags[f] {
   140  			if !wroteFlag {
   141  				ctx.WriteString("(")
   142  				wroteFlag = true
   143  			} else {
   144  				ctx.WriteString(", ")
   145  			}
   146  			ctx.WriteString(f.String())
   147  		}
   148  	}
   149  	if wroteFlag {
   150  		ctx.WriteString(") ")
   151  	}
   152  	ctx.FormatNode(node.Statement)
   153  }
   154  
   155  // ExplainAnalyzeDebug represents an EXPLAIN ANALYZE (DEBUG) statement. It is a
   156  // different node type than Explain to allow easier special treatment in the SQL
   157  // layer.
   158  type ExplainAnalyzeDebug struct {
   159  	Statement Statement
   160  }
   161  
   162  // Format implements the NodeFormatter interface.
   163  func (node *ExplainAnalyzeDebug) Format(ctx *FmtCtx) {
   164  	ctx.WriteString("EXPLAIN ANALYZE (DEBUG) ")
   165  	ctx.FormatNode(node.Statement)
   166  }
   167  
   168  // MakeExplain parses the EXPLAIN option strings and generates an explain
   169  // statement.
   170  func MakeExplain(options []string, stmt Statement) (Statement, error) {
   171  	for i := range options {
   172  		options[i] = strings.ToUpper(options[i])
   173  	}
   174  	find := func(o string) bool {
   175  		for i := range options {
   176  			if options[i] == o {
   177  				return true
   178  			}
   179  		}
   180  		return false
   181  	}
   182  
   183  	if find("DEBUG") {
   184  		if !find("ANALYZE") {
   185  			return nil, pgerror.Newf(pgcode.Syntax, "DEBUG flag can only be used with EXPLAIN ANALYZE")
   186  		}
   187  		if len(options) != 2 {
   188  			return nil, pgerror.Newf(
   189  				pgcode.Syntax, "EXPLAIN ANALYZE (DEBUG) cannot be used in conjunction with other flags")
   190  		}
   191  		return &ExplainAnalyzeDebug{Statement: stmt}, nil
   192  	}
   193  
   194  	var opts ExplainOptions
   195  	for _, opt := range options {
   196  		opt = strings.ToUpper(opt)
   197  		if m, ok := explainModeStringMap[opt]; ok {
   198  			if opts.Mode != 0 {
   199  				return nil, pgerror.Newf(pgcode.Syntax, "cannot set EXPLAIN mode more than once: %s", opt)
   200  			}
   201  			opts.Mode = m
   202  			continue
   203  		}
   204  		flag, ok := explainFlagStringMap[opt]
   205  		if !ok {
   206  			return nil, pgerror.Newf(pgcode.Syntax, "unsupported EXPLAIN option: %s", opt)
   207  		}
   208  		opts.Flags[flag] = true
   209  	}
   210  	if opts.Mode == 0 {
   211  		// Default mode is ExplainPlan.
   212  		opts.Mode = ExplainPlan
   213  	}
   214  	return &Explain{
   215  		ExplainOptions: opts,
   216  		Statement:      stmt,
   217  	}, nil
   218  }