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 }