Skip to content

Commit 17b728e

Browse files
authored
Add actual check of --error command not just that an error exists (#52)
1 parent 77628a8 commit 17b728e

File tree

10 files changed

+7359
-18
lines changed

10 files changed

+7359
-18
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mysql-tester
2+
gen_perror
13
### STS ###
24
.apt_generated
35
.classpath

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@ default: build
55
build:
66
go build -o mysql-tester ./src
77

8-
test:
8+
debug:
9+
go build -gcflags="all=-N -l" -o mysql-tester ./src
10+
11+
test: build
912
go test -cover ./...
13+
#./mysql-tester -check-error
1014

1115
tidy:
1216
go mod tidy
@@ -15,3 +19,5 @@ clean:
1519
go clean -i ./...
1620
rm -rf mysql-tester
1721

22+
gen_perror: generate_perror/main.go
23+
go build -o gen_perror ./generate_perror

generate_perror/main.go

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright 2023 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 main
15+
16+
import (
17+
"bufio"
18+
"flag"
19+
"fmt"
20+
"log"
21+
"os"
22+
"os/exec"
23+
"regexp"
24+
"sort"
25+
"strconv"
26+
)
27+
28+
const (
29+
fileHeader = `// Copyright 2023 PingCAP, Inc.
30+
//
31+
// Licensed under the Apache License, Version 2.0 (the "License");
32+
// you may not use this file except in compliance with the License.
33+
// You may obtain a copy of the License at
34+
//
35+
// http://www.apache.org/licenses/LICENSE-2.0
36+
//
37+
// Unless required by applicable law or agreed to in writing, software
38+
// distributed under the License is distributed on an "AS IS" BASIS,
39+
// See the License for the specific language governing permissions and
40+
// limitations under the License.
41+
42+
// Generated by generate_perror/main.go
43+
package main
44+
45+
var MysqlErrNameToNum = map[string]int{
46+
`
47+
)
48+
49+
var (
50+
tidbCodePath string
51+
)
52+
53+
func init() {
54+
flag.StringVar(&tidbCodePath, "path", "../tidb", "Path to TiDB source code root directory.")
55+
}
56+
57+
func checkNewErr(errCode string, i int, nameToNum map[string]int) {
58+
if v, ok := nameToNum[errCode]; ok {
59+
if i != v {
60+
if errCode != "handler" {
61+
// Ignore the HA_ERR codes, which are all 'handler'
62+
log.Printf("Duplicate error errCode %s (%d != %d)", errCode, i, v)
63+
}
64+
}
65+
} else {
66+
nameToNum[errCode] = i
67+
}
68+
}
69+
70+
func scanErrCodeFile(fileName string, nameToNum map[string]int) {
71+
f, err := os.Open(fileName)
72+
if err != nil {
73+
log.Fatal(err)
74+
}
75+
defer f.Close()
76+
s := bufio.NewScanner(f)
77+
r := regexp.MustCompile(`^\s+(\w+)*\s+=\s+(\d+)$`)
78+
for s.Scan() {
79+
m := r.FindStringSubmatch(s.Text())
80+
if m != nil && len(m) == 3 && m[1] != "" && m[2] != "" {
81+
i, err := strconv.Atoi(m[2])
82+
if err != nil {
83+
log.Fatal(err)
84+
}
85+
checkNewErr(m[1], i, nameToNum)
86+
}
87+
}
88+
if s.Err() != nil {
89+
log.Fatal(s.Err())
90+
}
91+
}
92+
93+
type ErrorCodeAndName struct {
94+
Code int
95+
Name string
96+
}
97+
98+
func main() {
99+
NameToNum := make(map[string]int)
100+
101+
// First extract the known error names => numbers from TiDB errno module
102+
103+
// Second extract the known error names => numbers from TiDB parser/mysql module
104+
105+
// Last use the perror program to extract error names from 1..20000 from MySQL
106+
107+
flag.Parse()
108+
109+
scanErrCodeFile(filepath.Join(tidbCodePath, "/pkg/errno/errcode.go"), NameToNum)
110+
errnoCodes := len(NameToNum)
111+
log.Printf("Got %d error codes from errno/errcode.go!", errnoCodes)
112+
scanErrCodeFile(tidbCodePath+"/pkg/parser/mysql/errcode.go", NameToNum)
113+
parserCodes := len(NameToNum) - errnoCodes
114+
log.Printf("Got %d New error codes from parser/mysql/errcode.go!", parserCodes)
115+
116+
// similar to:
117+
//seq 1 100000 | xargs perror 2> /dev/null | grep '^MySQL error code MY-[0-9]* ([A-Z_]*).*' | sed 's/^MySQL error code MY-0*\([[:digit:]]*\) (\([^)]*\)).*/"\2": \1,/'
118+
maxError := 20000
119+
log.Printf("Running perror for error codes 1..%d, may take some time...", maxError)
120+
for i := 1; i <= maxError; i++ {
121+
if i%1000 == 0 {
122+
fmt.Printf("\r%d", i)
123+
}
124+
cmd := exec.Command("perror", strconv.Itoa(i))
125+
stdout, err := cmd.StdoutPipe()
126+
if err != nil {
127+
log.Fatal(err)
128+
}
129+
if err = cmd.Start(); err != nil {
130+
log.Fatal(err)
131+
}
132+
s := bufio.NewScanner(stdout)
133+
r := regexp.MustCompile(`^MySQL error code MY-0*(\d+) \((\w+)\)`)
134+
for s.Scan() {
135+
m := r.FindStringSubmatch(s.Text())
136+
if m != nil && len(m) == 3 && m[1] != "" && m[2] != "" {
137+
c, err := strconv.Atoi(m[1])
138+
if err != nil {
139+
log.Fatal(err)
140+
}
141+
if c != i {
142+
log.Fatalf("perror gave error with wrong number? (Want: %d Got: %d)", i, c)
143+
}
144+
checkNewErr(m[2], i, NameToNum)
145+
}
146+
}
147+
cmd.Wait()
148+
}
149+
if maxError >= 1000 {
150+
fmt.Printf("\r")
151+
}
152+
log.Printf("Got %d New error codes from perror!", len(NameToNum)-parserCodes-errnoCodes)
153+
f, err := os.Create("perror.go")
154+
if err != nil {
155+
log.Fatal(err)
156+
}
157+
defer f.Close()
158+
w := bufio.NewWriter(f)
159+
_, err = w.WriteString(fileHeader)
160+
161+
if err != nil {
162+
log.Fatal(err)
163+
}
164+
165+
codes := make([]ErrorCodeAndName, 0, len(NameToNum))
166+
for k, v := range NameToNum {
167+
codes = append(codes, ErrorCodeAndName{Code: v, Name: k})
168+
}
169+
sort.Slice(codes, func(i, j int) bool {
170+
return codes[i].Code < codes[j].Code || codes[i].Code == codes[j].Code && codes[i].Name < codes[j].Name
171+
})
172+
for i, _ := range codes {
173+
_, err = w.WriteString("\t\"" + codes[i].Name + `": ` + strconv.Itoa(codes[i].Code) + ",\n")
174+
if err != nil {
175+
log.Fatal(err)
176+
}
177+
}
178+
_, err = w.WriteString("}\n")
179+
if err != nil {
180+
log.Fatal(err)
181+
}
182+
w.Flush()
183+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/pingcap/mysql-tester
22

3-
go 1.19
3+
go 1.21
44

55
require (
66
// It forks from github.com/go-sql-driver/mysql v1.7.1

r/example.result

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@ insert into t values(1, 1), (2, 2), (3, 3), (4, 4), (5, 5);
33
select * from t where a = 1;
44
a b
55
1 1
6+
SELECT 1 FROM NON_EXISTING_TABLE;
7+
Error 1146 (42S02): Table 'example.NON_EXISTING_TABLE' doesn't exist
8+
SELECT 2 FROM NON_EXISTING_TABLE;
9+
SELECT 3 FROM NON_EXISTING_TABLE;
10+
Got one of the listed errors
11+
SELECT 4;
12+
4
13+
4
14+
SELECT 5;
15+
5
16+
5
17+
SELECT 6;
18+
6
19+
6
20+
1 SELECT;
21+
Error 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 1 near "1 SELECT;"
22+
2 SELECT;
23+
3 SELECT;
24+
Got one of the listed errors
625
explain analyze format='brief' select * from t;
726
id estRows actRows task access object execution info operator info memory disk
827
TableReader 10000.00 5 root NULL time:<num>, loops:<num>, RU:<num>, cop_task: {num:<num>, max:<num>, proc_keys:<num>, rpc_num:<num>, rpc_time:<num>, copr_cache_hit_ratio:<num>, build_task_duration:<num>, max_distsql_concurrency:<num>} data:TableFullScan <num> Bytes N/A
@@ -26,3 +45,4 @@ info:
2645
INSERT t1 VALUES (1, 1), (1, 1) ON DUPLICATE KEY UPDATE f1 = 2, f2 = 2;
2746
affected rows: 3
2847
info: Records: 2 Duplicates: 1 Warnings: 0
48+
use `test`;;

r/extensions.result

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SELECT 1 FROM NON_EXISTING_TABLE;
2+
Got one of the listed errors

0 commit comments

Comments
 (0)