github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/sem/tree/format.go (about) 1 // Copyright 2016 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 "bytes" 15 "fmt" 16 "strings" 17 "sync" 18 "time" 19 20 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/lexbase" 21 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/sessiondatapb" 22 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/types" 23 "github.com/cockroachdb/cockroachdb-parser/pkg/util" 24 "github.com/cockroachdb/cockroachdb-parser/pkg/util/uuid" 25 "github.com/cockroachdb/errors" 26 "github.com/cockroachdb/redact" 27 ) 28 29 // FmtFlags carries options for the pretty-printer. 30 type FmtFlags int 31 32 // HasFlags tests whether the given flags are all set. 33 func (f FmtFlags) HasFlags(subset FmtFlags) bool { 34 return f&subset == subset 35 } 36 37 // HasAnyFlags tests whether any of the given flags are all set. 38 func (f FmtFlags) HasAnyFlags(subset FmtFlags) bool { 39 return f&subset != 0 40 } 41 42 // EncodeFlags returns the subset of the flags that are also lex encode flags. 43 func (f FmtFlags) EncodeFlags() lexbase.EncodeFlags { 44 return lexbase.EncodeFlags(f) & (lexbase.EncFirstFreeFlagBit - 1) 45 } 46 47 // Basic bit definitions for the FmtFlags bitmask. 48 const ( 49 // FmtSimple instructs the pretty-printer to produce 50 // a straightforward representation. 51 FmtSimple FmtFlags = 0 52 53 // FmtBareStrings instructs the pretty-printer to print strings and 54 // other values without wrapping quotes. If the value is a SQL 55 // string, the quotes will only be omitted if the string contains no 56 // special characters. If it does contain special characters, the 57 // string will be escaped and enclosed in e'...' regardless of 58 // whether FmtBareStrings is specified. See FmtRawStrings below for 59 // an alternative. 60 FmtBareStrings = FmtFlags(lexbase.EncBareStrings) 61 62 // FmtBareIdentifiers instructs the pretty-printer to print 63 // identifiers without wrapping quotes in any case. 64 FmtBareIdentifiers = FmtFlags(lexbase.EncBareIdentifiers) 65 66 // FmtShowPasswords instructs the pretty-printer to not suppress passwords. 67 // If not set, passwords are replaced by *****. 68 FmtShowPasswords = FmtFlags(lexbase.EncFirstFreeFlagBit) << iota 69 70 // FmtShowTypes instructs the pretty-printer to 71 // annotate expressions with their resolved types. 72 FmtShowTypes 73 74 // FmtHideConstants instructs the pretty-printer to produce a 75 // representation that does not disclose query-specific data. It 76 // also shorten long lists in tuples, VALUES and array expressions. 77 FmtHideConstants 78 79 // FmtAnonymize instructs the pretty-printer to remove any name. 80 FmtAnonymize 81 82 // FmtAlwaysQualifyTableNames instructs the pretty-printer to 83 // qualify table names, even if originally omitted. 84 // Requires Annotations in the formatting context. 85 FmtAlwaysQualifyTableNames 86 87 // FmtAlwaysGroupExprs instructs the pretty-printer to enclose 88 // sub-expressions between parentheses. 89 // Used for testing. 90 FmtAlwaysGroupExprs 91 92 // FmtShowTableAliases reveals the table aliases. 93 FmtShowTableAliases 94 95 // FmtSymbolicSubqueries indicates that subqueries must be pretty-printed 96 // using numeric notation (@S123). 97 FmtSymbolicSubqueries 98 99 // If set, strings will be formatted using the postgres datum-to-text 100 // conversion. See comments in pgwire_encode.go. 101 // Used internally in combination with FmtPgwireText defined below. 102 fmtPgwireFormat 103 104 // If set, datums and placeholders will have type annotations (like 105 // :::interval) as necessary to disambiguate between possible type 106 // resolutions. 107 fmtDisambiguateDatumTypes 108 109 // fmtSymbolicVars indicates that IndexedVars must be pretty-printed 110 // using numeric notation (@123). 111 fmtSymbolicVars 112 113 // fmtUnicodeStrings prints strings and JSON using the Go string 114 // formatter. This is used e.g. for emitting values to CSV files. 115 fmtRawStrings 116 117 // FmtParsableNumerics produces decimal and float representations 118 // that are always parsable, even if they require a string 119 // representation like -Inf. Negative values are preserved "inside" 120 // the numeric by enclosing them within parentheses. 121 FmtParsableNumerics 122 123 // FmtPGCatalog is used to produce expressions formatted in a way that's as 124 // close as possible to what clients expect to live in pg_catalog (e.g. 125 // pg_attrdef.adbin, pg_constraint.condef and pg_indexes.indexdef columns). 126 // Specifically, this strips type annotations (Postgres doesn't know what 127 // those are), adds cast expressions for non-numeric constants, and formats 128 // indexes in Postgres-specific syntax. 129 FmtPGCatalog 130 131 // If set, user defined types and datums of user defined types will be 132 // formatted in a way that is stable across changes to the underlying type. 133 // For type names, this means that they will be formatted as '@id'. For enum 134 // members, this means that they will be serialized as their bytes physical 135 // representations. 136 fmtStaticallyFormatUserDefinedTypes 137 138 // fmtFormatByteLiterals instructs bytes to be formatted as byte literals 139 // rather than string literals. For example, the bytes \x40ab will be formatted 140 // as x'40ab' rather than '\x40ab'. 141 fmtFormatByteLiterals 142 143 // FmtMarkRedactionNode instructs the pretty printer to redact datums, 144 // constants, and simple names (i.e. Name, UnrestrictedName) from statements. 145 FmtMarkRedactionNode 146 147 // FmtSummary instructs the pretty printer to produced a summarized version 148 // of the query, to pass to the frontend. 149 // 150 // Here are the following formats we support: 151 // SELECT: SELECT {columns} FROM {tables} 152 // - Show columns up to 15 characters. 153 // - Show tables up to 30 characters. 154 // - Hide column names in nested select queries. 155 // INSERT/UPSERT: INSERT/UPSERT INTO {table} {columns} 156 // - Show table up to 30 characters. 157 // - Show columns up to 15 characters. 158 // INSERT SELECT: INSERT INTO {table} SELECT {columns} FROM {table} 159 // - Show table up to 30 characters. 160 // - Show columns up to 15 characters. 161 // UPDATE: UPDATE {table} SET {columns} WHERE {condition} 162 // - Show table up to 30 characters. 163 // - Show columns up to 15 characters. 164 // - Show condition up to 15 characters. 165 FmtSummary 166 167 // FmtOmitNameRedaction instructs the pretty printer to omit redaction 168 // for simple names (i.e. Name, UnrestrictedName) from statements. 169 // This flag *overrides* `FmtMarkRedactionNode` above. 170 FmtOmitNameRedaction 171 172 // FmtTagDollarQuotes instructs tags to be kept intact in tagged dollar 173 // quotes. It also applies tags when formatting UDFs. 174 FmtTagDollarQuotes 175 ) 176 177 // PasswordSubstitution is the string that replaces 178 // passwords unless FmtShowPasswords is specified. 179 const PasswordSubstitution = "'*****'" 180 181 // ColumnLimit is the max character limit for columns in summarized queries 182 const ColumnLimit = 15 183 184 // TableLimit is the max character limit for tables in summarized queries 185 const TableLimit = 30 186 187 // Composite/derived flag definitions follow. 188 const ( 189 // FmtPgwireText instructs the pretty-printer to use 190 // a pg-compatible conversion to strings. See comments 191 // in pgwire_encode.go. 192 FmtPgwireText = fmtPgwireFormat | FmtFlags(lexbase.EncBareStrings) 193 194 // FmtParsable instructs the pretty-printer to produce a representation that 195 // can be parsed into an equivalent expression. If there is a chance that the 196 // formatted data will be stored durably on disk or sent to other nodes, 197 // then this formatting directive is not appropriate, and FmtSerializable 198 // should be used instead. 199 FmtParsable = fmtDisambiguateDatumTypes | FmtParsableNumerics 200 201 // FmtSerializable instructs the pretty-printer to produce a representation 202 // for expressions that can be serialized to disk. It serializes user defined 203 // types using representations that are stable across changes of the type 204 // itself. This should be used when serializing expressions that will be 205 // stored on disk, like DEFAULT expressions of columns. 206 FmtSerializable = FmtParsable | fmtStaticallyFormatUserDefinedTypes 207 208 // FmtCheckEquivalence instructs the pretty-printer to produce a representation 209 // that can be used to check equivalence of expressions. Specifically: 210 // - IndexedVars are formatted using symbolic notation (to disambiguate 211 // columns). 212 // - datum types are disambiguated with explicit type 213 // annotations. This is necessary because datums of different types 214 // can otherwise be formatted to the same string: (for example the 215 // DDecimal 1 and the DInt 1). 216 // - user defined types and datums of user defined types are formatted 217 // using static representations to avoid name resolution and invalidation 218 // due to changes in the underlying type. 219 FmtCheckEquivalence = fmtSymbolicVars | 220 fmtDisambiguateDatumTypes | 221 FmtParsableNumerics | 222 fmtStaticallyFormatUserDefinedTypes 223 224 // FmtArrayToString is a special composite flag suitable 225 // for the output of array_to_string(). This de-quotes 226 // the strings enclosed in the array and skips the normal escaping 227 // of strings. Special characters are hex-escaped. 228 FmtArrayToString = FmtBareStrings | fmtRawStrings 229 230 // FmtExport, if set, formats datums in a raw form suitable for 231 // EXPORT, e.g. suitable for output into a CSV file. The intended 232 // goal for this flag is to ensure values can be read back using the 233 // ParseDatumStringAs() / ParseStringas() functions (IMPORT). 234 // 235 // We do not use FmtParsable for this purpose because FmtParsable 236 // intends to preserve all the information useful to CockroachDB 237 // internally, at the expense of readability by 3rd party tools. 238 // 239 // We also separate this set of flag from fmtArrayToString 240 // because the behavior of array_to_string() is fixed for compatibility 241 // with PostgreSQL, whereas EXPORT may evolve over time to support 242 // other things (eg. fixing #33429). 243 FmtExport = FmtBareStrings | fmtRawStrings 244 ) 245 246 const flagsRequiringAnnotations = FmtAlwaysQualifyTableNames 247 248 // FmtCtx is suitable for passing to Format() methods. 249 // It also exposes the underlying bytes.Buffer interface for 250 // convenience. 251 // 252 // FmtCtx cannot be copied by value. 253 type FmtCtx struct { 254 _ util.NoCopy 255 256 bytes.Buffer 257 258 dataConversionConfig sessiondatapb.DataConversionConfig 259 location *time.Location 260 261 // NOTE: if you add more flags to this structure, make sure to add 262 // corresponding cleanup code in FmtCtx.Close(). 263 264 // The flags to use for pretty-printing. 265 flags FmtFlags 266 // AST Annotations (used by some flags). Can be unset if those flags are not 267 // used. 268 ann *Annotations 269 // indexedVarFormat is an optional interceptor for 270 // IndexedVarContainer.IndexedVarFormat calls; it can be used to 271 // customize the formatting of IndexedVars. 272 indexedVarFormat func(ctx *FmtCtx, idx int) 273 // tableNameFormatter will be called on all TableNames if it is non-nil. 274 tableNameFormatter func(*FmtCtx, *TableName) 275 // placeholderFormat is an optional interceptor for Placeholder.Format calls; 276 // it can be used to format placeholders differently than normal. 277 placeholderFormat func(ctx *FmtCtx, p *Placeholder) 278 // indexedTypeFormatter is an optional interceptor for formatting 279 // IDTypeReferences differently than normal. 280 indexedTypeFormatter func(*FmtCtx, *OIDTypeReference) 281 // small scratch buffer to reduce allocations. 282 scratch [64]byte 283 } 284 285 // FmtCtxOption is an option to pass into NewFmtCtx. 286 type FmtCtxOption func(*FmtCtx) 287 288 // FmtAnnotations adds annotations to the FmtCtx. 289 func FmtAnnotations(ann *Annotations) FmtCtxOption { 290 return func(ctx *FmtCtx) { 291 ctx.ann = ann 292 } 293 } 294 295 // FmtIndexedVarFormat modifies FmtCtx to customize the printing of 296 // IndexedVars using the provided function. 297 func FmtIndexedVarFormat(fn func(ctx *FmtCtx, idx int)) FmtCtxOption { 298 return func(ctx *FmtCtx) { 299 ctx.indexedVarFormat = fn 300 } 301 } 302 303 // FmtPlaceholderFormat modifies FmtCtx to customize the printing of 304 // StarDatums using the provided function. 305 func FmtPlaceholderFormat(placeholderFn func(_ *FmtCtx, _ *Placeholder)) FmtCtxOption { 306 return func(ctx *FmtCtx) { 307 ctx.placeholderFormat = placeholderFn 308 } 309 } 310 311 // FmtReformatTableNames modifies FmtCtx to substitute the printing of table 312 // naFmtParsable using the provided function. 313 func FmtReformatTableNames(tableNameFmt func(*FmtCtx, *TableName)) FmtCtxOption { 314 return func(ctx *FmtCtx) { 315 ctx.tableNameFormatter = tableNameFmt 316 } 317 } 318 319 // FmtIndexedTypeFormat modifies FmtCtx to customize the printing of 320 // IDTypeReferences using the provided function. 321 func FmtIndexedTypeFormat(fn func(*FmtCtx, *OIDTypeReference)) FmtCtxOption { 322 return func(ctx *FmtCtx) { 323 ctx.indexedTypeFormatter = fn 324 } 325 } 326 327 // FmtDataConversionConfig modifies FmtCtx to contain items relevant for the 328 // given DataConversionConfig. 329 func FmtDataConversionConfig(dcc sessiondatapb.DataConversionConfig) FmtCtxOption { 330 return func(ctx *FmtCtx) { 331 ctx.dataConversionConfig = dcc 332 } 333 } 334 335 // FmtLocation modifies FmtCtx to contain the correct location. 336 func FmtLocation(loc *time.Location) FmtCtxOption { 337 return func(ctx *FmtCtx) { 338 ctx.location = loc 339 } 340 } 341 342 // NewFmtCtx creates a FmtCtx; only flags that don't require Annotations 343 // can be used. 344 func NewFmtCtx(f FmtFlags, opts ...FmtCtxOption) *FmtCtx { 345 ctx := fmtCtxPool.Get().(*FmtCtx) 346 ctx.flags = f 347 for _, opts := range opts { 348 opts(ctx) 349 } 350 if ctx.ann == nil && f&flagsRequiringAnnotations != 0 { 351 panic(errors.AssertionFailedf("no Annotations provided")) 352 } 353 return ctx 354 } 355 356 // SetDataConversionConfig sets the DataConversionConfig on ctx and returns the 357 // old one. 358 func (ctx *FmtCtx) SetDataConversionConfig( 359 dcc sessiondatapb.DataConversionConfig, 360 ) sessiondatapb.DataConversionConfig { 361 old := ctx.dataConversionConfig 362 ctx.dataConversionConfig = dcc 363 return old 364 } 365 366 // SetLocation sets the location on ctx and returns the old one. 367 func (ctx *FmtCtx) SetLocation(loc *time.Location) *time.Location { 368 old := ctx.location 369 ctx.location = loc 370 return old 371 } 372 373 // WithReformatTableNames modifies FmtCtx to to substitute the printing of table 374 // names using the provided function, calls fn, then restores the original table 375 // formatting. 376 func (ctx *FmtCtx) WithReformatTableNames(tableNameFmt func(*FmtCtx, *TableName), fn func()) { 377 old := ctx.tableNameFormatter 378 ctx.tableNameFormatter = tableNameFmt 379 defer func() { ctx.tableNameFormatter = old }() 380 381 fn() 382 } 383 384 // WithFlags changes the flags in the FmtCtx, runs the given function, then 385 // restores the old flags. 386 func (ctx *FmtCtx) WithFlags(flags FmtFlags, fn func()) { 387 if ctx.ann == nil && flags&flagsRequiringAnnotations != 0 { 388 panic(errors.AssertionFailedf("no Annotations provided")) 389 } 390 oldFlags := ctx.flags 391 ctx.flags = flags 392 defer func() { ctx.flags = oldFlags }() 393 394 fn() 395 } 396 397 // HasFlags returns true iff the given flags are set in the formatter context. 398 func (ctx *FmtCtx) HasFlags(f FmtFlags) bool { 399 return ctx.flags.HasFlags(f) 400 } 401 402 // Printf calls fmt.Fprintf on the linked bytes.Buffer. It is provided 403 // for convenience, to avoid having to call fmt.Fprintf(&ctx.Buffer, ...). 404 // 405 // Note: DO NOT USE THIS TO INTERPOLATE %s ON NodeFormatter OBJECTS. 406 // This would call the String() method on them and would fail to reuse 407 // the same bytes buffer (and waste allocations). Instead use 408 // ctx.FormatNode(). 409 func (ctx *FmtCtx) Printf(f string, args ...interface{}) { 410 fmt.Fprintf(&ctx.Buffer, f, args...) 411 } 412 413 // NodeFormatter is implemented by nodes that can be pretty-printed. 414 type NodeFormatter interface { 415 // Format performs pretty-printing towards a bytes buffer. The flags member 416 // of ctx influences the results. Most callers should use FormatNode instead. 417 Format(ctx *FmtCtx) 418 } 419 420 // FormatName formats a string as a name. 421 // 422 // Note: prefer FormatNameP below when the string is already on the 423 // heap. 424 func (ctx *FmtCtx) FormatName(s string) { 425 ctx.FormatNode((*Name)(&s)) 426 } 427 428 // FormatNameP formats a string reference as a name. 429 func (ctx *FmtCtx) FormatNameP(s *string) { 430 ctx.FormatNode((*Name)(s)) 431 } 432 433 // FormatNode recurses into a node for pretty-printing. 434 // Flag-driven special cases can hook into this. 435 func (ctx *FmtCtx) FormatNode(n NodeFormatter) { 436 f := ctx.flags 437 if f.HasFlags(FmtShowTypes) { 438 if te, ok := n.(TypedExpr); ok { 439 ctx.WriteByte('(') 440 441 if f.HasFlags(FmtMarkRedactionNode) { 442 ctx.formatNodeMaybeMarkRedaction(n) 443 } else { 444 ctx.formatNodeOrHideConstants(n) 445 } 446 447 ctx.WriteString(")[") 448 if rt := te.ResolvedType(); rt == nil { 449 // An attempt is made to pretty-print an expression that was 450 // not assigned a type yet. This should not happen, so we make 451 // it clear in the output this needs to be investigated 452 // further. 453 ctx.Printf("??? %v", te) 454 } else { 455 ctx.WriteString(rt.String()) 456 } 457 ctx.WriteByte(']') 458 return 459 } 460 } 461 462 callFuncExpr := func(e Expr) bool { 463 f, ok := e.(*FuncExpr) 464 return ok && f.InCall 465 } 466 467 if f.HasFlags(FmtAlwaysGroupExprs) { 468 if e, ok := n.(Expr); ok && !callFuncExpr(e) { 469 ctx.WriteByte('(') 470 } 471 } 472 473 if f.HasFlags(FmtMarkRedactionNode) { 474 ctx.formatNodeMaybeMarkRedaction(n) 475 } else { 476 ctx.formatNodeOrHideConstants(n) 477 } 478 479 if f.HasFlags(FmtAlwaysGroupExprs) { 480 if e, ok := n.(Expr); ok && !callFuncExpr(e) { 481 ctx.WriteByte(')') 482 } 483 } 484 if f.HasAnyFlags(fmtDisambiguateDatumTypes | FmtPGCatalog) { 485 var typ *types.T 486 if d, isDatum := n.(Datum); isDatum { 487 if p, isPlaceholder := d.(*Placeholder); isPlaceholder { 488 // p.typ will be nil if the placeholder has not been type-checked yet. 489 typ = p.typ 490 } else if d.AmbiguousFormat() { 491 typ = d.ResolvedType() 492 } else if _, isArray := d.(*DArray); isArray && f.HasFlags(FmtPGCatalog) { 493 typ = d.ResolvedType() 494 } 495 } 496 if typ != nil { 497 if f.HasFlags(fmtDisambiguateDatumTypes) { 498 ctx.WriteString(":::") 499 ctx.FormatTypeReference(typ) 500 } else if f.HasFlags(FmtPGCatalog) && !typ.IsNumeric() { 501 ctx.WriteString("::") 502 ctx.FormatTypeReference(typ) 503 } 504 } 505 } 506 } 507 508 // formatLimitLength recurses into a node for pretty-printing, but limits the 509 // number of characters to be printed. 510 func (ctx *FmtCtx) formatLimitLength(n NodeFormatter, maxLength int) { 511 temp := NewFmtCtx(ctx.flags) 512 temp.FormatNodeSummary(n) 513 s := temp.CloseAndGetString() 514 if len(s) > maxLength { 515 truncated := s[:maxLength] + "..." 516 // close all open parentheses. 517 if strings.Count(truncated, "(") > strings.Count(truncated, ")") { 518 remaining := s[maxLength:] 519 for i, c := range remaining { 520 if c == ')' { 521 truncated += ")" 522 // add ellipses if there was more text after the parenthesis in 523 // the original string. 524 if i < len(remaining)-1 && string(remaining[i+1]) != ")" { 525 truncated += "..." 526 } 527 if strings.Count(truncated, "(") <= strings.Count(truncated, ")") { 528 break 529 } 530 } 531 } 532 } 533 s = truncated 534 } 535 ctx.WriteString(s) 536 } 537 538 // formatSummarySelect pretty-prints a summarized select statement. 539 // See FmtSummary for supported formats. 540 func (ctx *FmtCtx) formatSummarySelect(node *Select) { 541 if node.With == nil { 542 s := node.Select 543 if s, ok := s.(*SelectClause); ok { 544 ctx.WriteString("SELECT ") 545 ctx.formatLimitLength(&s.Exprs, ColumnLimit) 546 if len(s.From.Tables) > 0 { 547 ctx.WriteByte(' ') 548 ctx.formatLimitLength(&s.From, TableLimit+len("FROM ")) 549 } 550 } 551 } 552 } 553 554 // formatSummaryInsert pretty-prints a summarized insert/upsert statement. 555 // See FmtSummary for supported formats. 556 func (ctx *FmtCtx) formatSummaryInsert(node *Insert) { 557 if node.OnConflict.IsUpsertAlias() { 558 ctx.WriteString("UPSERT") 559 } else { 560 ctx.WriteString("INSERT") 561 } 562 ctx.WriteString(" INTO ") 563 ctx.formatLimitLength(node.Table, TableLimit) 564 rows := node.Rows 565 expr := rows.Select 566 if _, ok := expr.(*SelectClause); ok { 567 ctx.WriteByte(' ') 568 ctx.FormatNodeSummary(rows) 569 } else if node.Columns != nil { 570 ctx.WriteByte('(') 571 ctx.formatLimitLength(&node.Columns, ColumnLimit) 572 ctx.WriteByte(')') 573 } 574 } 575 576 // formatSummaryUpdate pretty-prints a summarized update statement. 577 // See FmtSummary for supported formats. 578 func (ctx *FmtCtx) formatSummaryUpdate(node *Update) { 579 if node.With == nil { 580 ctx.WriteString("UPDATE ") 581 ctx.formatLimitLength(node.Table, TableLimit) 582 ctx.WriteString(" SET ") 583 ctx.formatLimitLength(&node.Exprs, ColumnLimit) 584 if node.Where != nil { 585 ctx.WriteByte(' ') 586 ctx.formatLimitLength(node.Where, ColumnLimit+len("WHERE ")) 587 } 588 } 589 } 590 591 // FormatNodeSummary recurses into a node for pretty-printing a summarized version. 592 func (ctx *FmtCtx) FormatNodeSummary(n NodeFormatter) { 593 switch node := n.(type) { 594 case *Insert: 595 ctx.formatSummaryInsert(node) 596 return 597 case *Select: 598 ctx.formatSummarySelect(node) 599 return 600 case *Update: 601 ctx.formatSummaryUpdate(node) 602 return 603 } 604 ctx.FormatNode(n) 605 } 606 607 // AsStringWithFlags pretty prints a node to a string given specific flags; only 608 // flags that don't require Annotations can be used. 609 func AsStringWithFlags(n NodeFormatter, fl FmtFlags, opts ...FmtCtxOption) string { 610 ctx := NewFmtCtx(fl, opts...) 611 if fl.HasFlags(FmtSummary) { 612 ctx.FormatNodeSummary(n) 613 } else { 614 ctx.FormatNode(n) 615 } 616 return ctx.CloseAndGetString() 617 } 618 619 // AsStringWithFQNames pretty prints a node to a string with the 620 // FmtAlwaysQualifyTableNames flag (which requires annotations). 621 func AsStringWithFQNames(n NodeFormatter, ann *Annotations) string { 622 ctx := NewFmtCtx(FmtAlwaysQualifyTableNames, FmtAnnotations(ann)) 623 ctx.FormatNode(n) 624 return ctx.CloseAndGetString() 625 } 626 627 // AsString pretty prints a node to a string. 628 func AsString(n NodeFormatter) string { 629 return AsStringWithFlags(n, FmtSimple) 630 } 631 632 // ErrString pretty prints a node to a string. Identifiers are not quoted. 633 func ErrString(n NodeFormatter) string { 634 return AsStringWithFlags(n, FmtBareIdentifiers) 635 } 636 637 // Serialize pretty prints a node to a string using FmtSerializable; it is 638 // appropriate when we store expressions into strings that are stored on disk 639 // and may be later parsed back into expressions. 640 func Serialize(n NodeFormatter) string { 641 return AsStringWithFlags(n, FmtSerializable) 642 } 643 644 // SerializeForDisplay pretty prints a node to a string using FmtParsable. 645 // It is appropriate when printing expressions that are visible to end users. 646 func SerializeForDisplay(n NodeFormatter) string { 647 return AsStringWithFlags(n, FmtParsable) 648 } 649 650 var fmtCtxPool = sync.Pool{ 651 New: func() interface{} { 652 return &FmtCtx{} 653 }, 654 } 655 656 // Close releases a FmtCtx for reuse. Closing a FmtCtx is not required, but is 657 // recommended for performance-sensitive paths. 658 func (ctx *FmtCtx) Close() { 659 ctx.Buffer.Reset() 660 *ctx = FmtCtx{ 661 Buffer: ctx.Buffer, 662 } 663 fmtCtxPool.Put(ctx) 664 } 665 666 // CloseAndGetString combines Close() and String(). 667 func (ctx *FmtCtx) CloseAndGetString() string { 668 s := ctx.String() 669 ctx.Close() 670 return s 671 } 672 673 func (ctx *FmtCtx) alwaysFormatTablePrefix() bool { 674 return ctx.flags.HasFlags(FmtAlwaysQualifyTableNames) || ctx.tableNameFormatter != nil 675 } 676 677 // formatNodeMaybeMarkRedaction marks sensitive information from statements 678 // with redaction markers. Redaction markers are placed around datums, 679 // constants, and simple names (i.e. Name, UnrestrictedName). 680 func (ctx *FmtCtx) formatNodeMaybeMarkRedaction(n NodeFormatter) { 681 if ctx.flags.HasFlags(FmtMarkRedactionNode) { 682 switch v := n.(type) { 683 case *Placeholder: 684 // Placeholders should be printed as placeholder markers. 685 // Deliberately empty so we format as normal. 686 case *Name, *UnrestrictedName: 687 if ctx.flags.HasFlags(FmtOmitNameRedaction) { 688 break 689 } 690 ctx.WriteString(string(redact.StartMarker())) 691 v.Format(ctx) 692 ctx.WriteString(string(redact.EndMarker())) 693 return 694 case Datum, Constant: 695 ctx.WriteString(string(redact.StartMarker())) 696 v.Format(ctx) 697 ctx.WriteString(string(redact.EndMarker())) 698 return 699 } 700 n.Format(ctx) 701 } 702 } 703 704 func init() { 705 ctx := NewFmtCtx(FmtSimple) 706 if len(ctx.scratch) < uuid.RFC4122StrSize { 707 panic(errors.AssertionFailedf("FmtCtx scratch must be at least %d bytes", uuid.RFC4122StrSize)) 708 } 709 ctx.Close() 710 }