-
Notifications
You must be signed in to change notification settings - Fork 2
/
sql_checker.go
156 lines (148 loc) · 3.98 KB
/
sql_checker.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
package sq
import (
xerr "github.com/goclub/error"
"regexp"
"strings"
)
type SQLChecker interface {
Check(checkSQL []string, execSQL string) (pass bool, refs string, err error)
TrackFail(debugID uint64, err error, reviews []string, query string, refs string)
}
type DefaultSQLChecker struct {
}
func (check DefaultSQLChecker) Check(reviews []string, query string) (pass bool, refs string, err error) {
if len(reviews) == 0 {
return true, "", nil
}
for _, s := range reviews {
if s == query {
return true, "", nil
}
}
for _, format := range reviews {
matched, ref, err := check.match(query, format)
if err != nil {
return false, refs, err
}
refs += ref
if matched == true {
return true, "", nil
}
}
return false, refs, nil
}
func (check DefaultSQLChecker) TrackFail(debugID uint64, err error, reviews []string, query string, refs string) {
Log.Warn("DefaultSQLChecker Fail\n" + renderReview(debugID, query, reviews, refs))
}
type defaultSQLCheckerDifferent struct {
match bool
trimmedSQL string
trimmedFormat string
}
func (check DefaultSQLChecker) match(query string, format string) (matched bool, ref string, err error) {
trimmedFormat := format
trimmedSQL := query
// remove {#VALUES#} 和 (?,?),(?,?) 和 (?,?)
{
var reg *regexp.Regexp
reg, err = regexp.Compile(`VALUES \(.*\)`)
if err != nil {
return
}
trimmedSQL = reg.ReplaceAllString(trimmedSQL, "VALUES ")
trimmedFormat = strings.Replace(trimmedFormat, "{#VALUES#}", "", -1)
}
// remove {#IN#} 和 (?,?)
{
var reg *regexp.Regexp
reg, err = regexp.Compile(`\(\?(,\?)*?\)`)
if err != nil {
return
}
trimmedSQL = reg.ReplaceAllString(trimmedSQL, "")
trimmedFormat = strings.Replace(trimmedFormat, "{#IN#}", "", -1)
}
optional, err := check.matchCheckSQLOptional(trimmedFormat)
if err != nil {
return
}
for _, optionalItem := range optional {
trimmedFormat = strings.Replace(trimmedFormat, "{#"+optionalItem+"#}", "", 1)
}
for _, optionalItem := range optional {
trimmedSQL = strings.Replace(trimmedSQL, optionalItem, "", 1)
}
trimmedFormat = strings.TrimSpace(trimmedFormat)
trimmedSQL = strings.TrimSpace(trimmedSQL)
if trimmedSQL == trimmedFormat {
return true, "", nil
}
ref = "\n sql: \"" + trimmedSQL + "\"\nformat: \"" + trimmedFormat + "\""
return
}
// 匹配 QB{}.Review 中的 {# AND `name` = ?#} 部分并返回
func (check DefaultSQLChecker) matchCheckSQLOptional(str string) (optional []string, err error) {
strLen := len(str)
type Position struct {
Start int
End int
Done bool
}
data := []Position{}
for index, s := range str {
switch s {
case []rune("{")[0]:
// last rune
if index == strLen-1 {
continue
}
nextRune := str[index+1]
if nextRune == []byte("#")[0] {
// 检查之前是否出现 {# 但没有 #} 这种错误
if len(data) != 0 {
last := data[len(data)-1]
if last.Done == false {
message := "goclub/sql: SQLCheck missing #}\n" + str + "\n" +
strings.Repeat(" ", index) + "^"
return nil, xerr.New(message)
}
}
data = append(data, Position{
Start: index,
})
}
case []rune("#")[0]:
// last rune
if index == strLen-1 {
continue
}
nextRune := str[index+1]
if nextRune == []byte("}")[0] {
endIndex := index + 2
// 检查 #} 之前必须存在 {#
if len(data) == 0 {
return nil, xerr.New("goclub/sq;: SQLCheck missing {#\n" + str + "\n" +
strings.Repeat(" ", index) + "^")
}
last := data[len(data)-1]
if last.Done == true {
message := "goclub/sql: SQLCheck missing {#\n" + str + "\n" +
strings.Repeat(" ", index) + "^"
return nil, xerr.New(message)
}
last.End = endIndex
last.Done = true
data[len(data)-1] = last
}
}
}
for _, item := range data {
if item.Done == false {
message := "goclub/sql: SQLCheck missing #}\n" + str + "\n" +
strings.Repeat(" ", len(str)) + "^"
return nil, xerr.New(message)
}
optional = append(optional, str[item.Start+2:item.End-2])
}
return
}