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 }