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

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package parser
    15  
    16  import (
    17  	"strconv"
    18  	"strings"
    19  
    20  	"github.com/pingcap/tidb/parser/ast"
    21  	"github.com/pingcap/tidb/parser/mysql"
    22  	"github.com/pingcap/tidb/parser/terror"
    23  )
    24  
    25  //revive:disable:exported
    26  var (
    27  	ErrWarnOptimizerHintUnsupportedHint = terror.ClassParser.NewStd(mysql.ErrWarnOptimizerHintUnsupportedHint)
    28  	ErrWarnOptimizerHintInvalidToken    = terror.ClassParser.NewStd(mysql.ErrWarnOptimizerHintInvalidToken)
    29  	ErrWarnMemoryQuotaOverflow          = terror.ClassParser.NewStd(mysql.ErrWarnMemoryQuotaOverflow)
    30  	ErrWarnOptimizerHintParseError      = terror.ClassParser.NewStd(mysql.ErrWarnOptimizerHintParseError)
    31  	ErrWarnOptimizerHintInvalidInteger  = terror.ClassParser.NewStd(mysql.ErrWarnOptimizerHintInvalidInteger)
    32  	ErrWarnOptimizerHintWrongPos        = terror.ClassParser.NewStd(mysql.ErrWarnOptimizerHintWrongPos)
    33  )
    34  
    35  //revive:enable:exported
    36  
    37  // hintScanner implements the yyhintLexer interface
    38  type hintScanner struct {
    39  	Scanner
    40  }
    41  
    42  func (hs *hintScanner) Errorf(format string, args ...interface{}) error {
    43  	inner := hs.Scanner.Errorf(format, args...)
    44  	return ErrParse.GenWithStackByArgs("Optimizer hint syntax error at", inner)
    45  }
    46  
    47  func (hs *hintScanner) Lex(lval *yyhintSymType) int {
    48  	tok, pos, lit := hs.scan()
    49  	hs.lastScanOffset = pos.Offset
    50  	var errorTokenType string
    51  
    52  	switch tok {
    53  	case intLit:
    54  		n, e := strconv.ParseUint(lit, 10, 64)
    55  		if e != nil {
    56  			hs.AppendError(ErrWarnOptimizerHintInvalidInteger.GenWithStackByArgs(lit))
    57  			return hintInvalid
    58  		}
    59  		lval.number = n
    60  		return hintIntLit
    61  
    62  	case singleAtIdentifier:
    63  		lval.ident = lit
    64  		return hintSingleAtIdentifier
    65  
    66  	case identifier:
    67  		lval.ident = lit
    68  		if tok1, ok := hintTokenMap[strings.ToUpper(lit)]; ok {
    69  			return tok1
    70  		}
    71  		return hintIdentifier
    72  
    73  	case stringLit:
    74  		lval.ident = lit
    75  		if hs.sqlMode.HasANSIQuotesMode() && hs.r.s[pos.Offset] == '"' {
    76  			return hintIdentifier
    77  		}
    78  		return hintStringLit
    79  
    80  	case bitLit:
    81  		if strings.HasPrefix(lit, "0b") {
    82  			lval.ident = lit
    83  			return hintIdentifier
    84  		}
    85  		errorTokenType = "bit-value literal"
    86  
    87  	case hexLit:
    88  		if strings.HasPrefix(lit, "0x") {
    89  			lval.ident = lit
    90  			return hintIdentifier
    91  		}
    92  		errorTokenType = "hexadecimal literal"
    93  
    94  	case quotedIdentifier:
    95  		lval.ident = lit
    96  		return hintIdentifier
    97  
    98  	case eq:
    99  		return '='
   100  
   101  	case floatLit:
   102  		errorTokenType = "floating point number"
   103  	case decLit:
   104  		errorTokenType = "decimal number"
   105  
   106  	default:
   107  		if tok <= 0x7f {
   108  			return tok
   109  		}
   110  		errorTokenType = "unknown token"
   111  	}
   112  
   113  	hs.AppendError(ErrWarnOptimizerHintInvalidToken.GenWithStackByArgs(errorTokenType, lit, tok))
   114  	return hintInvalid
   115  }
   116  
   117  type hintParser struct {
   118  	lexer  hintScanner
   119  	result []*ast.TableOptimizerHint
   120  
   121  	// the following fields are used by yyParse to reduce allocation.
   122  	cache  []yyhintSymType
   123  	yylval yyhintSymType
   124  	yyVAL  *yyhintSymType
   125  }
   126  
   127  func newHintParser() *hintParser {
   128  	return &hintParser{cache: make([]yyhintSymType, 50)}
   129  }
   130  
   131  func (hp *hintParser) parse(input string, sqlMode mysql.SQLMode, initPos Pos) ([]*ast.TableOptimizerHint, []error) {
   132  	hp.result = nil
   133  	hp.lexer.reset(input[3:])
   134  	hp.lexer.SetSQLMode(sqlMode)
   135  	hp.lexer.r.updatePos(Pos{
   136  		Line:   initPos.Line,
   137  		Col:    initPos.Col + 3, // skipped the initial '/*+'
   138  		Offset: 0,
   139  	})
   140  	hp.lexer.inBangComment = true // skip the final '*/' (we need the '*/' for reporting warnings)
   141  
   142  	yyhintParse(&hp.lexer, hp)
   143  
   144  	warns, errs := hp.lexer.Errors()
   145  	if len(errs) == 0 {
   146  		errs = warns
   147  	}
   148  	return hp.result, errs
   149  }
   150  
   151  // ParseHint parses an optimizer hint (the interior of `/*+ ... */`).
   152  func ParseHint(input string, sqlMode mysql.SQLMode, initPos Pos) ([]*ast.TableOptimizerHint, []error) {
   153  	hp := newHintParser()
   154  	return hp.parse(input, sqlMode, initPos)
   155  }
   156  
   157  func (hp *hintParser) warnUnsupportedHint(name string) {
   158  	warn := ErrWarnOptimizerHintUnsupportedHint.GenWithStackByArgs(name)
   159  	hp.lexer.warns = append(hp.lexer.warns, warn)
   160  }
   161  
   162  func (hp *hintParser) lastErrorAsWarn() {
   163  	hp.lexer.lastErrorAsWarn()
   164  }