Skip to content

Commit 772552f

Browse files
authored
Merge pull request #687 from jmoiron/pr-501
merge #501 to trunk
2 parents 03c8d81 + ae5edc7 commit 772552f

File tree

3 files changed

+65
-8
lines changed

3 files changed

+65
-8
lines changed

README.md

+23-1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,28 @@ func main() {
182182
// as the name -> db mapping, so struct fields are lowercased and the `db` tag
183183
// is taken into consideration.
184184
rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)
185+
186+
187+
// batch insert
188+
189+
// batch insert with structs
190+
personStructs := []Person{
191+
{FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
192+
{FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
193+
{FirstName: "Ngani", LastName: "Laumape", Email: "nlaumape@ab.co.nz"},
194+
}
195+
196+
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
197+
VALUES (:first_name, :last_name, :email)`, personStructs)
198+
199+
// batch insert with maps
200+
personMaps := []map[string]interface{}{
201+
{"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
202+
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
203+
{"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
204+
}
205+
206+
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
207+
VALUES (:first_name, :last_name, :email)`, personMaps)
185208
}
186209
```
187-

named.go

+18-6
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,22 @@ func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
146146
}, nil
147147
}
148148

149+
// convertMapStringInterface attempts to convert v to map[string]interface{}.
150+
// Unlike v.(map[string]interface{}), this function works on named types that
151+
// are convertible to map[string]interface{} as well.
152+
func convertMapStringInterface(v interface{}) (map[string]interface{}, bool) {
153+
var m map[string]interface{}
154+
mtype := reflect.TypeOf(m)
155+
t := reflect.TypeOf(v)
156+
if !t.ConvertibleTo(mtype) {
157+
return nil, false
158+
}
159+
return reflect.ValueOf(v).Convert(mtype).Interface().(map[string]interface{}), true
160+
161+
}
162+
149163
func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
150-
if maparg, ok := arg.(map[string]interface{}); ok {
164+
if maparg, ok := convertMapStringInterface(arg); ok {
151165
return bindMapArgs(names, maparg)
152166
}
153167
return bindArgs(names, arg, m)
@@ -202,7 +216,7 @@ func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper)
202216
return "", []interface{}{}, err
203217
}
204218

205-
arglist, err := bindArgs(names, arg, m)
219+
arglist, err := bindAnyArgs(names, arg, m)
206220
if err != nil {
207221
return "", []interface{}{}, err
208222
}
@@ -383,12 +397,10 @@ func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Ma
383397
k := t.Kind()
384398
switch {
385399
case k == reflect.Map && t.Key().Kind() == reflect.String:
386-
var m map[string]interface{}
387-
if !t.ConvertibleTo(reflect.TypeOf(m)) {
400+
m, ok := convertMapStringInterface(arg)
401+
if !ok {
388402
return "", nil, fmt.Errorf("sqlx.bindNamedMapper: unsupported map type: %T", arg)
389403
}
390-
391-
m = reflect.ValueOf(arg).Convert(reflect.TypeOf(m)).Interface().(map[string]interface{})
392404
return bindMap(bindType, query, m)
393405
case k == reflect.Array || k == reflect.Slice:
394406
return bindArray(bindType, query, arg, m)

named_test.go

+24-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func TestNamedQueries(t *testing.T) {
195195
t.Errorf("got %s, expected %s", p.Email, people[0].Email)
196196
}
197197

198-
// test batch inserts
198+
// test struct batch inserts
199199
sls := []Person{
200200
{FirstName: "Ardie", LastName: "Savea", Email: "asavea@ab.co.nz"},
201201
{FirstName: "Sonny Bill", LastName: "Williams", Email: "sbw@ab.co.nz"},
@@ -206,6 +206,29 @@ func TestNamedQueries(t *testing.T) {
206206
_, err = db.NamedExec(insert, sls)
207207
test.Error(err)
208208

209+
// test map batch inserts
210+
slsMap := []map[string]interface{}{
211+
{"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
212+
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
213+
{"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
214+
}
215+
216+
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
217+
VALUES (:first_name, :last_name, :email)`, slsMap)
218+
test.Error(err)
219+
220+
type A map[string]interface{}
221+
222+
typedMap := []A{
223+
{"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
224+
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "sbw@ab.co.nz"},
225+
{"first_name": "Ngani", "last_name": "Laumape", "email": "nlaumape@ab.co.nz"},
226+
}
227+
228+
_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
229+
VALUES (:first_name, :last_name, :email)`, typedMap)
230+
test.Error(err)
231+
209232
for _, p := range sls {
210233
dest := Person{}
211234
err = db.Get(&dest, db.Rebind("SELECT * FROM person WHERE email=?"), p.Email)

0 commit comments

Comments
 (0)