Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit d76d6cf

Browse files
authored
go-sqlsmith: support hints in select/update/delete statement (#146)
Signed-off-by: Illyrix <Illyrix@outlook.com>
1 parent a2c6a00 commit d76d6cf

File tree

22 files changed

+419
-46
lines changed

22 files changed

+419
-46
lines changed

cmd/abtest/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func main() {
4444
pocketConfig.Options.Path = fixture.Context.ABTestConfig.LogPath
4545
pocketConfig.Options.Concurrency = fixture.Context.ABTestConfig.Concurrency
4646
pocketConfig.Options.GeneralLog = fixture.Context.ABTestConfig.GeneralLog
47+
pocketConfig.Options.EnableHint = fixture.Context.EnableHint
4748
suit := util.Suit{
4849
Config: &cfg,
4950
Provisioner: cluster.NewK8sProvisioner(),

cmd/cdc-pocket/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func main() {
4444
pocketConfig.Options.Concurrency = fixture.Context.ABTestConfig.Concurrency
4545
pocketConfig.Options.GeneralLog = fixture.Context.ABTestConfig.GeneralLog
4646
pocketConfig.Options.SyncTimeout.Duration = fixture.Context.BinlogConfig.SyncTimeout
47+
pocketConfig.Options.EnableHint = fixture.Context.EnableHint
4748
suit := util.Suit{
4849
Config: &cfg,
4950
Provisioner: cluster.NewK8sProvisioner(),

cmd/pocket/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func main() {
4444
pocketConfig.Options.Concurrency = fixture.Context.ABTestConfig.Concurrency
4545
pocketConfig.Options.GeneralLog = fixture.Context.ABTestConfig.GeneralLog
4646
pocketConfig.Options.SyncTimeout.Duration = fixture.Context.BinlogConfig.SyncTimeout
47+
pocketConfig.Options.EnableHint = fixture.Context.EnableHint
4748
suit := util.Suit{
4849
Config: &cfg,
4950
Provisioner: cluster.NewK8sProvisioner(),

cmd/tiflash-pocket/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func main() {
4141
pocketConfig := config.Init()
4242
pocketConfig.Options.Serialize = false
4343
pocketConfig.Options.Path = fixture.Context.TiFlashConfig.LogPath
44+
pocketConfig.Options.EnableHint = fixture.Context.EnableHint
4445
suit := util.Suit{
4546
Config: &cfg,
4647
Provisioner: cluster.NewK8sProvisioner(),

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ require (
1515
github.com/pingcap/chaos-mesh v0.0.0-20200221071630-a3e79a893072
1616
github.com/pingcap/errors v0.11.5-0.20190809092503-95897b64e011
1717
github.com/pingcap/go-tpc v0.0.0-20200229030315-98ee0f8f09d3
18-
github.com/pingcap/kvproto v0.0.0-20200102065152-5d51d93be892
19-
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9
18+
github.com/pingcap/kvproto v0.0.0-20200228095611-2cf9a243b8d5
19+
github.com/pingcap/log v0.0.0-20200117041106-d28c14d3b1cd
2020

21-
github.com/pingcap/parser v0.0.0-20200109073933-a9496438d77d
21+
github.com/pingcap/parser v0.0.0-20200317021010-cd90cc2a7d87
2222
github.com/pingcap/tidb v2.1.0-beta+incompatible
2323
github.com/pingcap/tidb-operator v1.1.0-beta.1.0.20200326133238-0fe67bf5e069
2424
github.com/rogpeppe/fastuuid v1.2.0
2525

2626
github.com/satori/go.uuid v1.2.0
2727
github.com/stretchr/testify v1.5.1
2828
github.com/uber-go/atomic v1.5.0 // indirect
29-
golang.org/x/net v0.0.0-20191112182307-2180aed22343
29+
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
3030
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
3131
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24 // indirect
3232
k8s.io/api v0.0.0
@@ -40,7 +40,7 @@ replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
4040

4141
replace github.com/pingcap/pd => github.com/pingcap/pd v1.1.0-beta.0.20200106144140-f5a7aa985497
4242

43-
replace github.com/pingcap/tidb => github.com/pingcap/tidb v1.1.0-beta.0.20200110034112-1b34cc234e82
43+
replace github.com/pingcap/tidb => github.com/pingcap/tidb v0.0.0-20200317142013-5268094afe05
4444

4545
replace github.com/prometheus/prometheus => github.com/prometheus/prometheus v1.8.2-0.20200213233353-b90be6f32a33
4646

go.sum

Lines changed: 112 additions & 20 deletions
Large diffs are not rendered by default.

pkg/go-sqlsmith/builtin/function.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
// GenerateFuncCallExpr generate random builtin chain
2525
func GenerateFuncCallExpr(table *types.Table, args int, stable bool) ast.ExprNode {
2626
if args == 0 && util.Rd(2) == 0 {
27-
return ast.NewValueExpr(util.GenerateRandDataItem())
27+
return ast.NewValueExpr(util.GenerateRandDataItem(), "", "")
2828
}
2929

3030
funcCallExpr := ast.FuncCallExpr{}

pkg/go-sqlsmith/builtin/hint.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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 builtin
15+
16+
import (
17+
"log"
18+
"math/rand"
19+
20+
"github.com/pingcap/parser/ast"
21+
"github.com/pingcap/parser/model"
22+
23+
"github.com/pingcap/tipocket/pkg/go-sqlsmith/types"
24+
"github.com/pingcap/tipocket/pkg/go-sqlsmith/util"
25+
)
26+
27+
type hintClass struct {
28+
name string
29+
minArg int
30+
maxArg int
31+
constArg bool
32+
mysql bool
33+
stable bool
34+
}
35+
36+
var hintKeywords = []*hintClass{
37+
// with no args
38+
{"hash_agg", 0, 0, false, false, false},
39+
{"stream_agg", 0, 0, false, false, false},
40+
{"agg_to_cop", 0, 0, false, false, false},
41+
{"read_consistent_replica", 0, 0, false, false, false},
42+
{"no_index_merge", 0, 0, false, false, false},
43+
44+
// with bool (TRUE or FALSE)
45+
{"use_toja", 1, 1, false, false, false},
46+
{"enable_plan_cache", 1, 1, false, false, false},
47+
{"use_cascades", 1, 1, false, false, false},
48+
49+
// these have been renamed
50+
// {"tidb_hj", 2, 3, false, false, true},
51+
// {"tidb_smj", 2, 3, false, false, true},
52+
// {"tidb_inlj", 2, 3, false, false, true},
53+
// with 2 or more args
54+
{"hash_join", 1, -1, false, true, false},
55+
{"merge_join", 1, -1, false, false, false},
56+
{"inl_join", 1, -1, false, false, false},
57+
58+
// with int (byte)
59+
{"memory_quota", 1, 1, false, false, false},
60+
// with int (ms)
61+
{"max_execution_time", 1, 1, false, false, false},
62+
}
63+
64+
var indexHintKeywords = []*hintClass{
65+
// with table name and at least one idx name
66+
{"use_index", 2, -1, false, false, false},
67+
{"ignore_index", 2, -1, false, false, false},
68+
{"use_index_merge", 2, -1, false, false, false},
69+
}
70+
71+
// these will not be generated for some reason
72+
var disabledHintKeywords = []*hintClass{
73+
{"qb_name", 0, 0, false, false, false},
74+
75+
// not released?
76+
{"time_range", 2, -1, false, false, false},
77+
// storage type with tablename: TIKV[t1]
78+
{"read_from_storage", 2, -1, false, false, false},
79+
// not released?
80+
{"query_type", 1, 1, false, false, false},
81+
82+
{"inl_hash_join", 1, -1, false, false, false},
83+
{"inl_merge_join", 1, -1, false, false, false},
84+
}
85+
86+
func GenerateHintExpr(table *types.Table) (h *ast.TableOptimizerHint) {
87+
enabledKeywords := hintKeywords
88+
if len(table.Indexes) > 0 {
89+
enabledKeywords = append(enabledKeywords, indexHintKeywords...)
90+
}
91+
h = new(ast.TableOptimizerHint)
92+
hintKeyword := enabledKeywords[util.Rd(len(enabledKeywords))]
93+
h.HintName = model.NewCIStr(hintKeyword.name)
94+
95+
if hintKeyword.maxArg == 0 {
96+
return
97+
}
98+
99+
if hintKeyword.maxArg == 1 {
100+
switch hintKeyword.name {
101+
case "use_toja", "enable_plan_cache", "use_cascades":
102+
h.HintData = util.RdBool()
103+
case "memory_quota":
104+
h.HintData = int64(util.RdRange(30720000, 40960000))
105+
case "max_execution_time":
106+
h.HintData = uint64(util.RdRange(500, 1500))
107+
default:
108+
log.Fatalf("unreachable hintKeyword.name:%s", hintKeyword.name)
109+
}
110+
return
111+
}
112+
113+
shuffledTables := make([]ast.HintTable, 0)
114+
for _, t := range table.InnerTableList {
115+
shuffledTables = append(shuffledTables, ast.HintTable{
116+
TableName: model.NewCIStr(t.Table),
117+
})
118+
}
119+
rand.Shuffle(len(shuffledTables), func(i, j int) {
120+
shuffledTables[i], shuffledTables[j] = shuffledTables[j], shuffledTables[i]
121+
})
122+
123+
shuffledIndexes := make([]model.CIStr, 0)
124+
for _, idx := range table.Indexes {
125+
if idx != "" {
126+
shuffledIndexes = append(shuffledIndexes, model.NewCIStr(idx))
127+
}
128+
}
129+
rand.Shuffle(len(shuffledIndexes), func(i, j int) {
130+
shuffledIndexes[i], shuffledIndexes[j] = shuffledIndexes[j], shuffledIndexes[i]
131+
})
132+
133+
switch hintKeyword.name {
134+
case "hash_join", "merge_join", "inl_join", "inl_hash_join", "inl_merge_join":
135+
if len(shuffledTables) < 2 {
136+
h = nil
137+
return
138+
}
139+
140+
n := util.MinInt(util.Rd(4)+2, len(shuffledTables)) // avoid case n < 2
141+
for ; n > 0; n-- {
142+
h.Tables = append(h.Tables, shuffledTables[n-1])
143+
}
144+
case "use_index", "ignore_index", "use_index_merge":
145+
// if no table nor index return empty
146+
if len(shuffledTables) == 0 || len(shuffledIndexes) == 0 {
147+
h = nil
148+
return
149+
}
150+
h.Tables = append(h.Tables, shuffledTables[util.Rd(len(shuffledTables))])
151+
n := util.MinInt(util.Rd(4)+1, len(shuffledIndexes)) // avoid case n == 0
152+
for ; n > 0; n-- {
153+
h.Indexes = append(h.Indexes, shuffledIndexes[n-1])
154+
}
155+
default:
156+
log.Fatalf("unreachable hintKeyword.name:%s", hintKeyword.name)
157+
}
158+
return
159+
}

pkg/go-sqlsmith/dml_ast.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func (s *SQLSmith) selectStmt(depth int) ast.Node {
4141
selectStmtNode.Where = s.binaryOperationExpr(util.Rd(depth), 1)
4242
}
4343

44+
selectStmtNode.TableHints = s.tableHintsExpr()
4445
selectStmtNode.From = s.tableRefsClause(depth)
4546

4647
return &selectStmtNode
@@ -66,9 +67,11 @@ func (s *SQLSmith) updateStmt() ast.Node {
6667
if whereRand < 8 {
6768
updateStmtNode.Where = s.binaryOperationExpr(whereRand, 0)
6869
} else {
69-
updateStmtNode.Where = ast.NewValueExpr(1)
70+
updateStmtNode.Where = ast.NewValueExpr(1, "", "")
7071
}
7172

73+
updateStmtNode.TableHints = s.tableHintsExpr()
74+
7275
return &updateStmtNode
7376
}
7477

@@ -94,12 +97,37 @@ func (s *SQLSmith) deleteStmt() ast.Node {
9497
if whereRand < 8 {
9598
deleteStmtNode.Where = s.binaryOperationExpr(whereRand, 0)
9699
} else {
97-
deleteStmtNode.Where = ast.NewValueExpr(1)
100+
deleteStmtNode.Where = ast.NewValueExpr(1, "", "")
98101
}
99102

103+
deleteStmtNode.TableHints = s.tableHintsExpr()
104+
100105
return &deleteStmtNode
101106
}
102107

108+
func (s *SQLSmith) tableHintsExpr() (hints []*ast.TableOptimizerHint) {
109+
if !s.Hint() {
110+
return
111+
}
112+
length := 0
113+
switch n := util.Rd(7)*util.Rd(7) - 17; {
114+
case n <= 0:
115+
length = 0
116+
case n < 4:
117+
length = 1
118+
case n < 9:
119+
length = 2
120+
case n < 14:
121+
length = 3
122+
default:
123+
length = 4
124+
}
125+
for i := 0; i < length; i++ {
126+
hints = append(hints, &ast.TableOptimizerHint{})
127+
}
128+
return
129+
}
130+
103131
func (s *SQLSmith) tableRefsClause(depth int) *ast.TableRefsClause {
104132
tableRefsClause := ast.TableRefsClause{
105133
TableRefs: &ast.Join{
@@ -177,7 +205,7 @@ func (s *SQLSmith) binaryOperationExpr(depth, complex int) ast.ExprNode {
177205
node.Op = opcode.EQ
178206
}
179207
node.L = &ast.ColumnNameExpr{}
180-
node.R = ast.NewValueExpr(1)
208+
node.R = ast.NewValueExpr(1, "", "")
181209
}
182210
}
183211
return &node
@@ -189,7 +217,7 @@ func (s *SQLSmith) patternInExpr() *ast.PatternInExpr {
189217
// case *ast.SubqueryExpr:
190218
// // may need refine after fully support of ResultSetNode interface
191219
// node.Query.(*ast.SelectStmt).Limit = &ast.Limit {
192-
// Count: ast.NewValueExpr(1),
220+
// Count: ast.NewValueExpr(1, "", ""),
193221
// }
194222
// }
195223

@@ -215,7 +243,7 @@ func (s *SQLSmith) exprNode(cons bool) ast.ExprNode {
215243
default:
216244
// hope there is an empty value type
217245
if cons {
218-
return ast.NewValueExpr(1)
246+
return ast.NewValueExpr(1, "", "")
219247
}
220248
return &ast.ColumnNameExpr{}
221249
}

pkg/go-sqlsmith/sqlsmith.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type SQLSmith struct {
3838
currDB string
3939
debug bool
4040
stable bool
41+
hint bool
4142
}
4243

4344
// New create SQLSmith instance
@@ -87,6 +88,14 @@ func (s *SQLSmith) SetStable(stable bool) {
8788
s.stable = stable
8889
}
8990

91+
func (s *SQLSmith) Hint() bool {
92+
return s.hint
93+
}
94+
95+
func (s *SQLSmith) SetHint(hint bool) {
96+
s.hint = hint
97+
}
98+
9099
// Walk will walk the tree and fillin tables and columns data
91100
func (s *SQLSmith) Walk(tree ast.Node) (string, string, error) {
92101
node, table, err := stateflow.New(s.GetDB(s.currDB), s.stable).WalkTree(tree)

0 commit comments

Comments
 (0)