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  }