Skip to content

Commit

Permalink
Merge pull request #687 from jmoiron/pr-501
Browse files Browse the repository at this point in the history
merge #501 to trunk
  • Loading branch information
jmoiron authored Jan 24, 2021
2 parents 03c8d81 + ae5edc7 commit 772552f
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 8 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,28 @@ func main() {
// as the name -> db mapping, so struct fields are lowercased and the `db` tag
// is taken into consideration.
rows, err = db.NamedQuery(`SELECT * FROM person WHERE first_name=:first_name`, jason)


// batch insert

// batch insert with structs
personStructs := []Person{
{FirstName: "Ardie", LastName: "Savea", Email: "[email protected]"},
{FirstName: "Sonny Bill", LastName: "Williams", Email: "[email protected]"},
{FirstName: "Ngani", LastName: "Laumape", Email: "[email protected]"},
}

_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personStructs)

// batch insert with maps
personMaps := []map[string]interface{}{
{"first_name": "Ardie", "last_name": "Savea", "email": "[email protected]"},
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "[email protected]"},
{"first_name": "Ngani", "last_name": "Laumape", "email": "[email protected]"},
}

_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, personMaps)
}
```

24 changes: 18 additions & 6 deletions named.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,22 @@ func prepareNamed(p namedPreparer, query string) (*NamedStmt, error) {
}, nil
}

// convertMapStringInterface attempts to convert v to map[string]interface{}.
// Unlike v.(map[string]interface{}), this function works on named types that
// are convertible to map[string]interface{} as well.
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) {
return nil, false
}
return reflect.ValueOf(v).Convert(mtype).Interface().(map[string]interface{}), true

}

func bindAnyArgs(names []string, arg interface{}, m *reflectx.Mapper) ([]interface{}, error) {
if maparg, ok := arg.(map[string]interface{}); ok {
if maparg, ok := convertMapStringInterface(arg); ok {
return bindMapArgs(names, maparg)
}
return bindArgs(names, arg, m)
Expand Down Expand Up @@ -202,7 +216,7 @@ func bindStruct(bindType int, query string, arg interface{}, m *reflectx.Mapper)
return "", []interface{}{}, err
}

arglist, err := bindArgs(names, arg, m)
arglist, err := bindAnyArgs(names, arg, m)
if err != nil {
return "", []interface{}{}, err
}
Expand Down Expand Up @@ -383,12 +397,10 @@ func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Ma
k := t.Kind()
switch {
case k == reflect.Map && t.Key().Kind() == reflect.String:
var m map[string]interface{}
if !t.ConvertibleTo(reflect.TypeOf(m)) {
m, ok := convertMapStringInterface(arg)
if !ok {
return "", nil, fmt.Errorf("sqlx.bindNamedMapper: unsupported map type: %T", arg)
}

m = reflect.ValueOf(arg).Convert(reflect.TypeOf(m)).Interface().(map[string]interface{})
return bindMap(bindType, query, m)
case k == reflect.Array || k == reflect.Slice:
return bindArray(bindType, query, arg, m)
Expand Down
25 changes: 24 additions & 1 deletion named_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func TestNamedQueries(t *testing.T) {
t.Errorf("got %s, expected %s", p.Email, people[0].Email)
}

// test batch inserts
// test struct batch inserts
sls := []Person{
{FirstName: "Ardie", LastName: "Savea", Email: "[email protected]"},
{FirstName: "Sonny Bill", LastName: "Williams", Email: "[email protected]"},
Expand All @@ -206,6 +206,29 @@ func TestNamedQueries(t *testing.T) {
_, err = db.NamedExec(insert, sls)
test.Error(err)

// test map batch inserts
slsMap := []map[string]interface{}{
{"first_name": "Ardie", "last_name": "Savea", "email": "[email protected]"},
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "[email protected]"},
{"first_name": "Ngani", "last_name": "Laumape", "email": "[email protected]"},
}

_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, slsMap)
test.Error(err)

type A map[string]interface{}

typedMap := []A{
{"first_name": "Ardie", "last_name": "Savea", "email": "[email protected]"},
{"first_name": "Sonny Bill", "last_name": "Williams", "email": "[email protected]"},
{"first_name": "Ngani", "last_name": "Laumape", "email": "[email protected]"},
}

_, err = db.NamedExec(`INSERT INTO person (first_name, last_name, email)
VALUES (:first_name, :last_name, :email)`, typedMap)
test.Error(err)

for _, p := range sls {
dest := Person{}
err = db.Get(&dest, db.Rebind("SELECT * FROM person WHERE email=?"), p.Email)
Expand Down

0 comments on commit 772552f

Please sign in to comment.