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