Skip to content

Commit 12007f2

Browse files
authored
Merge pull request jmoiron#282 from nussjustin/perf-in
Optimize In by specializing for concrete slice types
2 parents acf1cbb + 5608f0d commit 12007f2

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

bind.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,9 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
136136
}
137137

138138
newArgs := make([]interface{}, 0, flatArgsCount)
139+
buf := bytes.NewBuffer(make([]byte, 0, len(query)+len(", ?")*flatArgsCount))
139140

140141
var arg, offset int
141-
var buf bytes.Buffer
142142

143143
for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
144144
if arg >= len(meta) {
@@ -164,13 +164,12 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
164164
// write everything up to and including our ? character
165165
buf.WriteString(query[:offset+i+1])
166166

167-
newArgs = append(newArgs, argMeta.v.Index(0).Interface())
168-
169167
for si := 1; si < argMeta.length; si++ {
170168
buf.WriteString(", ?")
171-
newArgs = append(newArgs, argMeta.v.Index(si).Interface())
172169
}
173170

171+
newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)
172+
174173
// slice the query and reset the offset. this avoids some bookkeeping for
175174
// the write after the loop
176175
query = query[offset+i+1:]
@@ -185,3 +184,24 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
185184

186185
return buf.String(), newArgs, nil
187186
}
187+
188+
func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
189+
switch val := v.Interface().(type) {
190+
case []interface{}:
191+
args = append(args, val...)
192+
case []int:
193+
for i := range val {
194+
args = append(args, val[i])
195+
}
196+
case []string:
197+
for i := range val {
198+
args = append(args, val[i])
199+
}
200+
default:
201+
for si := 0; si < vlen; si++ {
202+
args = append(args, v.Index(si).Interface())
203+
}
204+
}
205+
206+
return args
207+
}

sqlx_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,36 @@ func BenchmarkIn(b *testing.B) {
17261726
}
17271727
}
17281728

1729+
func BenchmarkIn1k(b *testing.B) {
1730+
q := `SELECT * FROM foo WHERE x = ? AND v in (?) AND y = ?`
1731+
1732+
var vals [1000]interface{}
1733+
1734+
for i := 0; i < b.N; i++ {
1735+
_, _, _ = In(q, []interface{}{"foo", vals[:], "bar"}...)
1736+
}
1737+
}
1738+
1739+
func BenchmarkIn1kInt(b *testing.B) {
1740+
q := `SELECT * FROM foo WHERE x = ? AND v in (?) AND y = ?`
1741+
1742+
var vals [1000]int
1743+
1744+
for i := 0; i < b.N; i++ {
1745+
_, _, _ = In(q, []interface{}{"foo", vals[:], "bar"}...)
1746+
}
1747+
}
1748+
1749+
func BenchmarkIn1kString(b *testing.B) {
1750+
q := `SELECT * FROM foo WHERE x = ? AND v in (?) AND y = ?`
1751+
1752+
var vals [1000]string
1753+
1754+
for i := 0; i < b.N; i++ {
1755+
_, _, _ = In(q, []interface{}{"foo", vals[:], "bar"}...)
1756+
}
1757+
}
1758+
17291759
func BenchmarkRebind(b *testing.B) {
17301760
b.StopTimer()
17311761
q1 := `INSERT INTO foo (a, b, c, d, e, f, g, h, i) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`

0 commit comments

Comments
 (0)