github.com/team-ide/go-dialect@v1.9.20/vitess/sqlparser/parser.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package sqlparser
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"strconv"
    23  	"strings"
    24  	"sync"
    25  
    26  	"github.com/team-ide/go-dialect/vitess/vterrors"
    27  
    28  	vtrpcpb "github.com/team-ide/go-dialect/vitess/vtrpc"
    29  )
    30  
    31  var versionFlagSync sync.Once
    32  
    33  // parserPool is a pool for parser objects.
    34  var parserPool = sync.Pool{
    35  	New: func() interface{} {
    36  		return &yyParserImpl{}
    37  	},
    38  }
    39  
    40  // zeroParser is a zero-initialized parser to help reinitialize the parser for pooling.
    41  var zeroParser yyParserImpl
    42  
    43  // yyParsePooled is a wrapper around yyParse that pools the parser objects. There isn't a
    44  // particularly good reason to use yyParse directly, since it immediately discards its parser.
    45  //
    46  // N.B: Parser pooling means that you CANNOT take references directly to parse stack variables (e.g.
    47  // $$ = &$4) in sql.y rules. You must instead add an intermediate reference like so:
    48  //    showCollationFilterOpt := $4
    49  //    $$ = &Show{Type: string($2), ShowCollationFilterOpt: &showCollationFilterOpt}
    50  func yyParsePooled(yylex yyLexer) int {
    51  	parser := parserPool.Get().(*yyParserImpl)
    52  	defer func() {
    53  		*parser = zeroParser
    54  		parserPool.Put(parser)
    55  	}()
    56  	return parser.Parse(yylex)
    57  }
    58  
    59  // Instructions for creating new types: If a type
    60  // needs to satisfy an interface, declare that function
    61  // along with that interface. This will help users
    62  // identify the list of types to which they can assert
    63  // those interfaces.
    64  // If the member of a type has a string with a predefined
    65  // list of values, declare those values as const following
    66  // the type.
    67  // For interfaces that define dummy functions to consolidate
    68  // a set of types, define the function as iTypeName.
    69  // This will help avoid name collisions.
    70  
    71  // Parse2 parses the SQL in full and returns a Statement, which
    72  // is the AST representation of the query, and a set of BindVars, which are all the
    73  // bind variables that were found in the original SQL query. If a DDL statement
    74  // is partially parsed but still contains a syntax error, the
    75  // error is ignored and the DDL is returned anyway.
    76  func Parse2(sql string) (Statement, BindVars, error) {
    77  	tokenizer := NewStringTokenizer(sql)
    78  	if yyParsePooled(tokenizer) != 0 {
    79  		if tokenizer.partialDDL != nil {
    80  			if typ, val := tokenizer.Scan(); typ != 0 {
    81  				return nil, nil, fmt.Errorf("extra characters encountered after end of DDL: '%s'", string(val))
    82  			}
    83  			switch x := tokenizer.partialDDL.(type) {
    84  			case DBDDLStatement:
    85  				x.SetFullyParsed(false)
    86  			case DDLStatement:
    87  				x.SetFullyParsed(false)
    88  			}
    89  			tokenizer.ParseTree = tokenizer.partialDDL
    90  			return tokenizer.ParseTree, tokenizer.BindVars, nil
    91  		}
    92  		return nil, nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, tokenizer.LastError.Error())
    93  	}
    94  	if tokenizer.ParseTree == nil {
    95  		return nil, nil, ErrEmpty
    96  	}
    97  	return tokenizer.ParseTree, tokenizer.BindVars, nil
    98  }
    99  
   100  // convertMySQLVersionToCommentVersion converts the MySQL version into comment version format.
   101  func convertMySQLVersionToCommentVersion(version string) (string, error) {
   102  	var res = make([]int, 3)
   103  	idx := 0
   104  	val := ""
   105  	for _, c := range version {
   106  		if c <= '9' && c >= '0' {
   107  			val += string(c)
   108  		} else if c == '.' {
   109  			v, err := strconv.Atoi(val)
   110  			if err != nil {
   111  				return "", err
   112  			}
   113  			val = ""
   114  			res[idx] = v
   115  			idx++
   116  			if idx == 3 {
   117  				break
   118  			}
   119  		} else {
   120  			break
   121  		}
   122  	}
   123  	if val != "" {
   124  		v, err := strconv.Atoi(val)
   125  		if err != nil {
   126  			return "", err
   127  		}
   128  		res[idx] = v
   129  		idx++
   130  	}
   131  	if idx == 0 {
   132  		return "", vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "MySQL version not correctly setup - %s.", version)
   133  	}
   134  
   135  	return fmt.Sprintf("%01d%02d%02d", res[0], res[1], res[2]), nil
   136  }
   137  
   138  // Parse behaves like Parse2 but does not return a set of bind variables
   139  func Parse(sql string) (Statement, error) {
   140  	stmt, _, err := Parse2(sql)
   141  	return stmt, err
   142  }
   143  
   144  // ParseStrictDDL is the same as Parse except it errors on
   145  // partially parsed DDL statements.
   146  func ParseStrictDDL(sql string) (Statement, error) {
   147  	tokenizer := NewStringTokenizer(sql)
   148  	if yyParsePooled(tokenizer) != 0 {
   149  		return nil, tokenizer.LastError
   150  	}
   151  	if tokenizer.ParseTree == nil {
   152  		return nil, ErrEmpty
   153  	}
   154  	return tokenizer.ParseTree, nil
   155  }
   156  
   157  // ParseTokenizer is a raw interface to parse from the given tokenizer.
   158  // This does not used pooled parsers, and should not be used in general.
   159  func ParseTokenizer(tokenizer *Tokenizer) int {
   160  	return yyParse(tokenizer)
   161  }
   162  
   163  // ParseNext parses a single SQL statement from the tokenizer
   164  // returning a Statement which is the AST representation of the query.
   165  // The tokenizer will always read up to the end of the statement, allowing for
   166  // the next call to ParseNext to parse any subsequent SQL statements. When
   167  // there are no more statements to parse, a error of io.EOF is returned.
   168  func ParseNext(tokenizer *Tokenizer) (Statement, error) {
   169  	return parseNext(tokenizer, false)
   170  }
   171  
   172  // ParseNextStrictDDL is the same as ParseNext except it errors on
   173  // partially parsed DDL statements.
   174  func ParseNextStrictDDL(tokenizer *Tokenizer) (Statement, error) {
   175  	return parseNext(tokenizer, true)
   176  }
   177  
   178  func parseNext(tokenizer *Tokenizer, strict bool) (Statement, error) {
   179  	if tokenizer.cur() == ';' {
   180  		tokenizer.skip(1)
   181  		tokenizer.skipBlank()
   182  	}
   183  	if tokenizer.cur() == eofChar {
   184  		return nil, io.EOF
   185  	}
   186  
   187  	tokenizer.reset()
   188  	tokenizer.multi = true
   189  	if yyParsePooled(tokenizer) != 0 {
   190  		if tokenizer.partialDDL != nil && !strict {
   191  			tokenizer.ParseTree = tokenizer.partialDDL
   192  			return tokenizer.ParseTree, nil
   193  		}
   194  		return nil, tokenizer.LastError
   195  	}
   196  	if tokenizer.ParseTree == nil {
   197  		return ParseNext(tokenizer)
   198  	}
   199  	return tokenizer.ParseTree, nil
   200  }
   201  
   202  // ErrEmpty is a sentinel error returned when parsing empty statements.
   203  var ErrEmpty = vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.EmptyQuery, "query was empty")
   204  
   205  // SplitStatement returns the first sql statement up to either a ; or EOF
   206  // and the remainder from the given buffer
   207  func SplitStatement(blob string) (string, string, error) {
   208  	tokenizer := NewStringTokenizer(blob)
   209  	tkn := 0
   210  	for {
   211  		tkn, _ = tokenizer.Scan()
   212  		if tkn == 0 || tkn == ';' || tkn == eofChar {
   213  			break
   214  		}
   215  	}
   216  	if tokenizer.LastError != nil {
   217  		return "", "", tokenizer.LastError
   218  	}
   219  	if tkn == ';' {
   220  		return blob[:tokenizer.Pos-1], blob[tokenizer.Pos:], nil
   221  	}
   222  	return blob, "", nil
   223  }
   224  
   225  // SplitStatementToPieces split raw sql statement that may have multi sql pieces to sql pieces
   226  // returns the sql pieces blob contains; or error if sql cannot be parsed
   227  func SplitStatementToPieces(blob string) (pieces []string, err error) {
   228  	// fast path: the vast majority of SQL statements do not have semicolons in them
   229  	if blob == "" {
   230  		return nil, nil
   231  	}
   232  	switch strings.IndexByte(blob, ';') {
   233  	case -1: // if there is no semicolon, return blob as a whole
   234  		return []string{blob}, nil
   235  	case len(blob) - 1: // if there's a single semicolon and it's the last character, return blob without it
   236  		return []string{blob[:len(blob)-1]}, nil
   237  	}
   238  
   239  	pieces = make([]string, 0, 16)
   240  	tokenizer := NewStringTokenizer(blob)
   241  
   242  	tkn := 0
   243  	var stmt string
   244  	stmtBegin := 0
   245  	emptyStatement := true
   246  loop:
   247  	for {
   248  		tkn, _ = tokenizer.Scan()
   249  		switch tkn {
   250  		case ';':
   251  			stmt = blob[stmtBegin : tokenizer.Pos-1]
   252  			if !emptyStatement {
   253  				pieces = append(pieces, stmt)
   254  				emptyStatement = true
   255  			}
   256  			stmtBegin = tokenizer.Pos
   257  		case 0, eofChar:
   258  			blobTail := tokenizer.Pos - 1
   259  			if stmtBegin < blobTail {
   260  				stmt = blob[stmtBegin : blobTail+1]
   261  				if !emptyStatement {
   262  					pieces = append(pieces, stmt)
   263  				}
   264  			}
   265  			break loop
   266  		default:
   267  			emptyStatement = false
   268  		}
   269  	}
   270  
   271  	err = tokenizer.LastError
   272  	return
   273  }
   274  
   275  // String returns a string representation of an SQLNode.
   276  func String(node SQLNode) string {
   277  	if node == nil {
   278  		return "<nil>"
   279  	}
   280  
   281  	buf := NewTrackedBuffer(nil)
   282  	node.formatFast(buf)
   283  	return buf.String()
   284  }