Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions named.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,17 @@ func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
func convertMapStringInterface(v interface{}) (map[string]interface{}, bool) {
var m map[string]interface{}
mtype := reflect.TypeOf(m)
t := reflect.TypeOf(v)
if !t.ConvertibleTo(mtype) {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return nil, false
}
rv = rv.Elem()
}
if !rv.Type().ConvertibleTo(mtype) {
return nil, false
}
return reflect.ValueOf(v).Convert(mtype).Interface().(map[string]interface{}), true

return rv.Convert(mtype).Interface().(map[string]interface{}), true
}

func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
Expand Down Expand Up @@ -296,7 +301,7 @@ func bindArray(bindType int, query string, arg interface{}, m *reflectx.Mapper)
if err != nil {
return "", []interface{}{}, err
}
arrayValue := reflect.ValueOf(arg)
arrayValue := reflect.Indirect(reflect.ValueOf(arg))
arrayLen := arrayValue.Len()
if arrayLen == 0 {
return "", []interface{}{}, fmt.Errorf("length of array is 0: %#v", arg)
Expand Down Expand Up @@ -440,6 +445,12 @@ func Named(query string, arg interface{}) (string, []interface{}, error) {
func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
t := reflect.TypeOf(arg)
k := t.Kind()

if k == reflect.Ptr {
t = t.Elem()
k = t.Kind()
}

switch {
case k == reflect.Map && t.Key().Kind() == reflect.String:
m, ok := convertMapStringInterface(arg)
Expand Down
40 changes: 40 additions & 0 deletions named_ptr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package sqlx

import "testing"

// Named queries accept a pointer to a slice, to a map and to a slice of maps
// (upstream #577, #729).
func TestNamedPtrArgs(t *testing.T) {
RunWithSchema(defaultSchema, t, func(db *DB, t *testing.T, now string) {
const insert = "INSERT INTO person (first_name, last_name, email) VALUES (:first_name, :last_name, :email)"
try := func(label string, arg interface{}, want int64) {
defer func() {
if r := recover(); r != nil {
t.Errorf("%s: panicked: %v", label, r)
}
}()
res, err := db.NamedExec(insert, arg)
if err != nil {
t.Errorf("%s: %v", label, err)
return
}
if n, err := res.RowsAffected(); err == nil && n != want {
t.Errorf("%s: %d rows affected, want %d", label, n, want)
}
}
people := []Person{{FirstName: "A", LastName: "B", Email: "a@b"}, {FirstName: "C", LastName: "D", Email: "c@d"}}
try("*[]struct", &people, 2)
m := map[string]interface{}{"first_name": "E", "last_name": "F", "email": "e@f"}
try("*map", &m, 1)
ms := []map[string]interface{}{{"first_name": "G", "last_name": "H", "email": "g@h"}, {"first_name": "I", "last_name": "J", "email": "i@j"}}
try("*[]map", &ms, 2)

var count int
if err := db.Get(&count, "SELECT count(*) FROM person"); err != nil {
t.Fatal(err)
}
if count != 5 {
t.Errorf("expected 5 people inserted, got %d", count)
}
})
}
4 changes: 4 additions & 0 deletions named_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ func TestNamedQueries(t *testing.T) {
_, err = db.NamedExec(insert, sls)
test.Error(err)

// and test ptr batch inserts
_, err = db.NamedExec(insert, &sls)
test.Error(err)

// test map batch inserts
slsMap := []map[string]interface{}{
{"first_name": "Ardie", "last_name": "Savea", "email": "asavea@ab.co.nz"},
Expand Down
Loading