github.com/pingcap/tidb/parser@v0.0.0-20231013125129-93a834a6bf8d/format/format.go (about)

     1  // Copyright (c) 2014 The sortutil Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSES/STRUTIL-LICENSE file.
     4  
     5  // Copyright 2015 PingCAP, Inc.
     6  //
     7  // Licensed under the Apache License, Version 2.0 (the "License");
     8  // you may not use this file except in compliance with the License.
     9  // You may obtain a copy of the License at
    10  //
    11  //     http://www.apache.org/licenses/LICENSE-2.0
    12  //
    13  // Unless required by applicable law or agreed to in writing, software
    14  // distributed under the License is distributed on an "AS IS" BASIS,
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package format
    19  
    20  import (
    21  	"bytes"
    22  	"fmt"
    23  	"io"
    24  	"strings"
    25  )
    26  
    27  const (
    28  	st0 = iota
    29  	stBOL
    30  	stPERC
    31  	stBOLPERC
    32  )
    33  
    34  // Formatter is an io.Writer extended formatter by a fmt.Printf like function Format.
    35  type Formatter interface {
    36  	io.Writer
    37  	Format(format string, args ...interface{}) (n int, errno error)
    38  }
    39  
    40  type indentFormatter struct {
    41  	io.Writer
    42  	indent      []byte
    43  	indentLevel int
    44  	state       int
    45  }
    46  
    47  var replace = map[rune]string{
    48  	'\000': "\\0",
    49  	'\'':   "''",
    50  	'\n':   "\\n",
    51  	'\r':   "\\r",
    52  }
    53  
    54  // IndentFormatter returns a new Formatter which interprets %i and %u in the
    55  // Format() formats string as indent and unindent commands. The commands can
    56  // nest. The Formatter writes to io.Writer 'w' and inserts one 'indent'
    57  // string per current indent level value.
    58  // Behaviour of commands reaching negative indent levels is undefined.
    59  //
    60  //	IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
    61  //
    62  // output:
    63  //
    64  //	abc3%e
    65  //	    x
    66  //	    y
    67  //	z
    68  //
    69  // The Go quoted string literal form of the above is:
    70  //
    71  //	"abc%%e\n\tx\n\tx\nz\n"
    72  //
    73  // The commands can be scattered between separate invocations of Format(),
    74  // i.e. the formatter keeps track of the indent level and knows if it is
    75  // positioned on start of a line and should emit indentation(s).
    76  // The same output as above can be produced by e.g.:
    77  //
    78  //	f := IndentFormatter(os.Stdout, " ")
    79  //	f.Format("abc%d%%e%i\nx\n", 3)
    80  //	f.Format("y\n%uz\n")
    81  func IndentFormatter(w io.Writer, indent string) Formatter {
    82  	return &indentFormatter{w, []byte(indent), 0, stBOL}
    83  }
    84  
    85  func (f *indentFormatter) format(flat bool, format string, args ...interface{}) (n int, errno error) {
    86  	var buf = make([]byte, 0)
    87  	for i := 0; i < len(format); i++ {
    88  		c := format[i]
    89  		switch f.state {
    90  		case st0:
    91  			switch c {
    92  			case '\n':
    93  				cc := c
    94  				if flat && f.indentLevel != 0 {
    95  					cc = ' '
    96  				}
    97  				buf = append(buf, cc)
    98  				f.state = stBOL
    99  			case '%':
   100  				f.state = stPERC
   101  			default:
   102  				buf = append(buf, c)
   103  			}
   104  		case stBOL:
   105  			switch c {
   106  			case '\n':
   107  				cc := c
   108  				if flat && f.indentLevel != 0 {
   109  					cc = ' '
   110  				}
   111  				buf = append(buf, cc)
   112  			case '%':
   113  				f.state = stBOLPERC
   114  			default:
   115  				if !flat {
   116  					for i := 0; i < f.indentLevel; i++ {
   117  						buf = append(buf, f.indent...)
   118  					}
   119  				}
   120  				buf = append(buf, c)
   121  				f.state = st0
   122  			}
   123  		case stBOLPERC:
   124  			switch c {
   125  			case 'i':
   126  				f.indentLevel++
   127  				f.state = stBOL
   128  			case 'u':
   129  				f.indentLevel--
   130  				f.state = stBOL
   131  			default:
   132  				if !flat {
   133  					for i := 0; i < f.indentLevel; i++ {
   134  						buf = append(buf, f.indent...)
   135  					}
   136  				}
   137  				buf = append(buf, '%', c)
   138  				f.state = st0
   139  			}
   140  		case stPERC:
   141  			switch c {
   142  			case 'i':
   143  				f.indentLevel++
   144  				f.state = st0
   145  			case 'u':
   146  				f.indentLevel--
   147  				f.state = st0
   148  			default:
   149  				buf = append(buf, '%', c)
   150  				f.state = st0
   151  			}
   152  		default:
   153  			panic("unexpected state")
   154  		}
   155  	}
   156  	switch f.state {
   157  	case stPERC, stBOLPERC:
   158  		buf = append(buf, '%')
   159  	}
   160  	return fmt.Fprintf(f, string(buf), args...)
   161  }
   162  
   163  // Format implements Format interface.
   164  func (f *indentFormatter) Format(format string, args ...interface{}) (n int, errno error) {
   165  	return f.format(false, format, args...)
   166  }
   167  
   168  type flatFormatter indentFormatter
   169  
   170  // FlatFormatter returns a newly created Formatter with the same functionality as the one returned
   171  // by IndentFormatter except it allows a newline in the 'format' string argument of Format
   172  // to pass through if the indent level is current zero.
   173  //
   174  // If the indent level is non-zero then such new lines are changed to a space character.
   175  // There is no indent string, the %i and %u format verbs are used solely to determine the indent level.
   176  //
   177  // The FlatFormatter is intended for flattening of normally nested structure textual representation to
   178  // a one top level structure per line form.
   179  //
   180  //	FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
   181  //
   182  // output in the form of a Go quoted string literal:
   183  //
   184  //	"abc3%%e x y z\n"
   185  func FlatFormatter(w io.Writer) Formatter {
   186  	return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter))
   187  }
   188  
   189  // Format implements Format interface.
   190  func (f *flatFormatter) Format(format string, args ...interface{}) (n int, errno error) {
   191  	return (*indentFormatter)(f).format(true, format, args...)
   192  }
   193  
   194  // OutputFormat output escape character with backslash.
   195  func OutputFormat(s string) string {
   196  	var buf bytes.Buffer
   197  	for _, old := range s {
   198  		if newVal, ok := replace[old]; ok {
   199  			buf.WriteString(newVal)
   200  			continue
   201  		}
   202  		buf.WriteRune(old)
   203  	}
   204  
   205  	return buf.String()
   206  }
   207  
   208  // RestoreFlags mark the Restore format
   209  type RestoreFlags uint64
   210  
   211  // Mutually exclusive group of `RestoreFlags`:
   212  // [RestoreStringSingleQuotes, RestoreStringDoubleQuotes]
   213  // [RestoreKeyWordUppercase, RestoreKeyWordLowercase]
   214  // [RestoreNameUppercase, RestoreNameLowercase]
   215  // [RestoreNameDoubleQuotes, RestoreNameBackQuotes]
   216  // The flag with the left position in each group has a higher priority.
   217  const (
   218  	RestoreStringSingleQuotes RestoreFlags = 1 << iota
   219  	RestoreStringDoubleQuotes
   220  	RestoreStringEscapeBackslash
   221  
   222  	RestoreKeyWordUppercase
   223  	RestoreKeyWordLowercase
   224  
   225  	RestoreNameUppercase
   226  	RestoreNameLowercase
   227  	RestoreNameDoubleQuotes
   228  	RestoreNameBackQuotes
   229  
   230  	RestoreSpacesAroundBinaryOperation
   231  	RestoreBracketAroundBinaryOperation
   232  
   233  	RestoreStringWithoutCharset
   234  	RestoreStringWithoutDefaultCharset
   235  
   236  	RestoreTiDBSpecialComment
   237  	SkipPlacementRuleForRestore
   238  	RestoreWithTTLEnableOff
   239  	RestoreWithoutSchemaName
   240  	RestoreWithoutTableName
   241  	RestoreForNonPrepPlanCache
   242  )
   243  
   244  const (
   245  	// DefaultRestoreFlags is the default value of RestoreFlags.
   246  	DefaultRestoreFlags = RestoreStringSingleQuotes | RestoreKeyWordUppercase | RestoreNameBackQuotes
   247  )
   248  
   249  func (rf RestoreFlags) has(flag RestoreFlags) bool {
   250  	return rf&flag != 0
   251  }
   252  
   253  // HasWithoutSchemaNameFlag returns a boolean indicating when `rf` has `RestoreWithoutSchemaName` flag.
   254  func (rf RestoreFlags) HasWithoutSchemaNameFlag() bool {
   255  	return rf.has(RestoreWithoutSchemaName)
   256  }
   257  
   258  // HasWithoutTableNameFlag returns a boolean indicating when `rf` has `RestoreWithoutTableName` flag.
   259  func (rf RestoreFlags) HasWithoutTableNameFlag() bool {
   260  	return rf.has(RestoreWithoutTableName)
   261  }
   262  
   263  // HasStringSingleQuotesFlag returns a boolean indicating when `rf` has `RestoreStringSingleQuotes` flag.
   264  func (rf RestoreFlags) HasStringSingleQuotesFlag() bool {
   265  	return rf.has(RestoreStringSingleQuotes)
   266  }
   267  
   268  // HasStringDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreStringDoubleQuotes` flag.
   269  func (rf RestoreFlags) HasStringDoubleQuotesFlag() bool {
   270  	return rf.has(RestoreStringDoubleQuotes)
   271  }
   272  
   273  // HasStringEscapeBackslashFlag returns a boolean indicating whether `rf` has `RestoreStringEscapeBackslash` flag.
   274  func (rf RestoreFlags) HasStringEscapeBackslashFlag() bool {
   275  	return rf.has(RestoreStringEscapeBackslash)
   276  }
   277  
   278  // HasKeyWordUppercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordUppercase` flag.
   279  func (rf RestoreFlags) HasKeyWordUppercaseFlag() bool {
   280  	return rf.has(RestoreKeyWordUppercase)
   281  }
   282  
   283  // HasKeyWordLowercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordLowercase` flag.
   284  func (rf RestoreFlags) HasKeyWordLowercaseFlag() bool {
   285  	return rf.has(RestoreKeyWordLowercase)
   286  }
   287  
   288  // HasNameUppercaseFlag returns a boolean indicating whether `rf` has `RestoreNameUppercase` flag.
   289  func (rf RestoreFlags) HasNameUppercaseFlag() bool {
   290  	return rf.has(RestoreNameUppercase)
   291  }
   292  
   293  // HasNameLowercaseFlag returns a boolean indicating whether `rf` has `RestoreNameLowercase` flag.
   294  func (rf RestoreFlags) HasNameLowercaseFlag() bool {
   295  	return rf.has(RestoreNameLowercase)
   296  }
   297  
   298  // HasNameDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameDoubleQuotes` flag.
   299  func (rf RestoreFlags) HasNameDoubleQuotesFlag() bool {
   300  	return rf.has(RestoreNameDoubleQuotes)
   301  }
   302  
   303  // HasNameBackQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameBackQuotes` flag.
   304  func (rf RestoreFlags) HasNameBackQuotesFlag() bool {
   305  	return rf.has(RestoreNameBackQuotes)
   306  }
   307  
   308  // HasSpacesAroundBinaryOperationFlag returns a boolean indicating
   309  // whether `rf` has `RestoreSpacesAroundBinaryOperation` flag.
   310  func (rf RestoreFlags) HasSpacesAroundBinaryOperationFlag() bool {
   311  	return rf.has(RestoreSpacesAroundBinaryOperation)
   312  }
   313  
   314  // HasRestoreBracketAroundBinaryOperation returns a boolean indicating
   315  // whether `rf` has `RestoreBracketAroundBinaryOperation` flag.
   316  func (rf RestoreFlags) HasRestoreBracketAroundBinaryOperation() bool {
   317  	return rf.has(RestoreBracketAroundBinaryOperation)
   318  }
   319  
   320  // HasStringWithoutDefaultCharset returns a boolean indicating
   321  // whether `rf` has `RestoreStringWithoutDefaultCharset` flag.
   322  func (rf RestoreFlags) HasStringWithoutDefaultCharset() bool {
   323  	return rf.has(RestoreStringWithoutDefaultCharset)
   324  }
   325  
   326  // HasStringWithoutCharset returns a boolean indicating whether `rf` has `RestoreStringWithoutCharset` flag.
   327  func (rf RestoreFlags) HasStringWithoutCharset() bool {
   328  	return rf.has(RestoreStringWithoutCharset)
   329  }
   330  
   331  // HasTiDBSpecialCommentFlag returns a boolean indicating whether `rf` has `RestoreTiDBSpecialComment` flag.
   332  func (rf RestoreFlags) HasTiDBSpecialCommentFlag() bool {
   333  	return rf.has(RestoreTiDBSpecialComment)
   334  }
   335  
   336  // HasSkipPlacementRuleForRestoreFlag returns a boolean indicating whether `rf` has `SkipPlacementRuleForRestore` flag.
   337  func (rf RestoreFlags) HasSkipPlacementRuleForRestoreFlag() bool {
   338  	return rf.has(SkipPlacementRuleForRestore)
   339  }
   340  
   341  // HasRestoreWithTTLEnableOff returns a boolean indicating
   342  // whether to force set TTL_ENABLE='OFF' when restoring a TTL table
   343  func (rf RestoreFlags) HasRestoreWithTTLEnableOff() bool {
   344  	return rf.has(RestoreWithTTLEnableOff)
   345  }
   346  
   347  // HasRestoreForNonPrepPlanCache returns a boolean indicating whether `rf` has `RestoreForNonPrepPlanCache` flag.
   348  func (rf RestoreFlags) HasRestoreForNonPrepPlanCache() bool {
   349  	return rf.has(RestoreForNonPrepPlanCache)
   350  }
   351  
   352  // RestoreWriter is the interface for `Restore` to write.
   353  type RestoreWriter interface {
   354  	io.Writer
   355  	io.StringWriter
   356  }
   357  
   358  // RestoreCtx is `Restore` context to hold flags and writer.
   359  type RestoreCtx struct {
   360  	Flags     RestoreFlags
   361  	In        RestoreWriter
   362  	DefaultDB string
   363  	CTERestorer
   364  }
   365  
   366  // NewRestoreCtx returns a new `RestoreCtx`.
   367  func NewRestoreCtx(flags RestoreFlags, in RestoreWriter) *RestoreCtx {
   368  	return &RestoreCtx{Flags: flags, In: in, DefaultDB: ""}
   369  }
   370  
   371  // WriteKeyWord writes the `keyWord` into writer.
   372  // `keyWord` will be converted format(uppercase and lowercase for now) according to `RestoreFlags`.
   373  func (ctx *RestoreCtx) WriteKeyWord(keyWord string) {
   374  	switch {
   375  	case ctx.Flags.HasKeyWordUppercaseFlag():
   376  		keyWord = strings.ToUpper(keyWord)
   377  	case ctx.Flags.HasKeyWordLowercaseFlag():
   378  		keyWord = strings.ToLower(keyWord)
   379  	}
   380  	ctx.In.WriteString(keyWord)
   381  }
   382  
   383  // WriteWithSpecialComments writes a string with a special comment wrapped.
   384  func (ctx *RestoreCtx) WriteWithSpecialComments(featureID string, fn func() error) error {
   385  	if !ctx.Flags.HasTiDBSpecialCommentFlag() {
   386  		return fn()
   387  	}
   388  	ctx.WritePlain("/*T!")
   389  	if len(featureID) != 0 {
   390  		ctx.WritePlainf("[%s]", featureID)
   391  	}
   392  	ctx.WritePlain(" ")
   393  	if err := fn(); err != nil {
   394  		return err
   395  	}
   396  	ctx.WritePlain(" */")
   397  	return nil
   398  }
   399  
   400  // WriteString writes the string into writer
   401  // `str` may be wrapped in quotes and escaped according to RestoreFlags.
   402  func (ctx *RestoreCtx) WriteString(str string) {
   403  	if ctx.Flags.HasStringEscapeBackslashFlag() {
   404  		str = strings.ReplaceAll(str, `\`, `\\`)
   405  	}
   406  	quotes := ""
   407  	switch {
   408  	case ctx.Flags.HasStringSingleQuotesFlag():
   409  		str = strings.ReplaceAll(str, `'`, `''`)
   410  		quotes = `'`
   411  	case ctx.Flags.HasStringDoubleQuotesFlag():
   412  		str = strings.ReplaceAll(str, `"`, `""`)
   413  		quotes = `"`
   414  	}
   415  	ctx.In.WriteString(quotes)
   416  	ctx.In.WriteString(str)
   417  	ctx.In.WriteString(quotes)
   418  }
   419  
   420  // WriteName writes the name into writer
   421  // `name` maybe wrapped in quotes and escaped according to RestoreFlags.
   422  func (ctx *RestoreCtx) WriteName(name string) {
   423  	switch {
   424  	case ctx.Flags.HasNameUppercaseFlag():
   425  		name = strings.ToUpper(name)
   426  	case ctx.Flags.HasNameLowercaseFlag():
   427  		name = strings.ToLower(name)
   428  	}
   429  	quotes := ""
   430  	switch {
   431  	case ctx.Flags.HasNameDoubleQuotesFlag():
   432  		name = strings.ReplaceAll(name, `"`, `""`)
   433  		quotes = `"`
   434  	case ctx.Flags.HasNameBackQuotesFlag():
   435  		name = strings.ReplaceAll(name, "`", "``")
   436  		quotes = "`"
   437  	}
   438  
   439  	// use `WriteString` directly instead of `fmt.Fprint` to get a better performance.
   440  	ctx.In.WriteString(quotes)
   441  	ctx.In.WriteString(name)
   442  	ctx.In.WriteString(quotes)
   443  }
   444  
   445  // WritePlain writes the plain text into writer without any handling.
   446  func (ctx *RestoreCtx) WritePlain(plainText string) {
   447  	ctx.In.WriteString(plainText)
   448  }
   449  
   450  // WritePlainf write the plain text into writer without any handling.
   451  func (ctx *RestoreCtx) WritePlainf(format string, a ...interface{}) {
   452  	fmt.Fprintf(ctx.In, format, a...)
   453  }
   454  
   455  // CTERestorer is used by WithClause related nodes restore.
   456  type CTERestorer struct {
   457  	CTENames []string
   458  }
   459  
   460  // IsCTETableName returns true if the given tableName comes from CTE.
   461  func (c *CTERestorer) IsCTETableName(nameL string) bool {
   462  	for _, n := range c.CTENames {
   463  		if n == nameL {
   464  			return true
   465  		}
   466  	}
   467  	return false
   468  }
   469  
   470  // RecordCTEName records the CTE name.
   471  func (c *CTERestorer) RecordCTEName(nameL string) {
   472  	c.CTENames = append(c.CTENames, nameL)
   473  }
   474  
   475  // RestoreCTEFunc is used to restore CTE.
   476  func (c *CTERestorer) RestoreCTEFunc() func() {
   477  	l := len(c.CTENames)
   478  	return func() {
   479  		if l == 0 {
   480  			c.CTENames = nil
   481  		} else {
   482  			c.CTENames = c.CTENames[:l]
   483  		}
   484  	}
   485  }