diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..c32de978 --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module gopkg.in/gorp.v2 + +go 1.13 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-sql-driver/mysql v1.4.1 + github.com/jmoiron/sqlx v1.2.0 + github.com/lib/pq v1.2.0 + github.com/mattn/go-sqlite3 v1.11.0 + github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 + github.com/stretchr/testify v1.4.0 + github.com/ziutek/mymysql v1.5.4 + google.golang.org/appengine v1.6.5 // indirect + gopkg.in/yaml.v2 v2.2.4 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..7395066a --- /dev/null +++ b/go.sum @@ -0,0 +1,39 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1 h1:oL4IBbcqwhhNWh31bjOX8C/OCy0zs9906d/VUru+bqg= +github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/gorp.go b/gorp.go index fc654567..8b0f3f1f 100644 --- a/gorp.go +++ b/gorp.go @@ -13,6 +13,8 @@ import ( "regexp" "strings" "time" + + "github.com/jmoiron/sqlx/reflectx" ) // OracleString (empty string is null) @@ -256,6 +258,8 @@ func expandNamedQuery(m *DbMap, query string, keyGetter func(key string) reflect }), args } +var dbMapper = reflectx.NewMapper("db") + func columnToFieldIndex(m *DbMap, t reflect.Type, name string, cols []string) ([][]int, error) { colToFieldIndex := make([][]int, len(cols)) @@ -267,37 +271,30 @@ func columnToFieldIndex(m *DbMap, t reflect.Type, name string, cols []string) ([ tableMapped = true } - // Loop over column names and find field in i to bind to - // based on column name. all returned columns must match - // a field in the i struct - missingColNames := []string{} + clower := make([]string, len(cols)) for x := range cols { - colName := strings.ToLower(cols[x]) - field, found := t.FieldByNameFunc(func(fieldName string) bool { - field, _ := t.FieldByName(fieldName) - cArguments := strings.Split(field.Tag.Get("db"), ",") - fieldName = cArguments[0] - - if fieldName == "-" { - return false - } else if fieldName == "" { - fieldName = field.Name + clower[x] = strings.ToLower(cols[x]) + if tableMapped { + colMap := colMapOrNil(table, clower[x]) + if colMap != nil { + clower[x] = colMap.ColumnName } - if tableMapped { - colMap := colMapOrNil(table, fieldName) - if colMap != nil { - fieldName = colMap.ColumnName - } - } - return colName == strings.ToLower(fieldName) - }) - if found { - colToFieldIndex[x] = field.Index } - if colToFieldIndex[x] == nil { - missingColNames = append(missingColNames, colName) + } + + missingColNames := []string{} + err := dbMapper.TraversalsByNameFunc(t, clower, func(x int, idx []int) error { + if len(idx) == 0 { + missingColNames = append(missingColNames, clower[x]) } + colToFieldIndex[x] = idx + return nil + }) + + if err != nil { + return nil, err } + if len(missingColNames) > 0 { return colToFieldIndex, &NoFieldInTypeError{ TypeName: t.Name(), diff --git a/mapping_test.go b/mapping_test.go new file mode 100644 index 00000000..31026a9b --- /dev/null +++ b/mapping_test.go @@ -0,0 +1,85 @@ +package gorp + +import ( + "reflect" + "testing" + "time" +) + +type testUser struct { + Id uint64 `db:"id"` + Username string `db:"user_name"` + HashedPassword []byte `db:"hashed_password"` + EMail string `db:"email"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` +} + +func BenchmarkCcolumnToFieldIndex(b *testing.B) { + structType := reflect.TypeOf(testUser{}) + dbmap := &DbMap{} + b.ResetTimer() + for n := 0; n < b.N; n++ { + _, err := columnToFieldIndex(dbmap, + structType, + "some_table", + []string{ + "user_name", + "email", + "created_at", + "updated_at", + "id", + }) + if err != nil { + panic(err) + } + } +} + +func TestColumnToFieldIndexBasic(t *testing.T) { + structType := reflect.TypeOf(testUser{}) + dbmap := &DbMap{} + cols, err := columnToFieldIndex(dbmap, + structType, + "some_table", + []string{ + "email", + }) + if err != nil { + t.Fatal(err) + } + if len(cols) != 1 { + t.Fatal("cols should have 1 result", cols) + } + if cols[0][0] != 3 { + t.Fatal("cols[0][0] should map to email field in testUser", cols) + } +} + +func TestColumnToFieldIndexSome(t *testing.T) { + structType := reflect.TypeOf(testUser{}) + dbmap := &DbMap{} + cols, err := columnToFieldIndex(dbmap, + structType, + "some_table", + []string{ + "id", + "email", + "created_at", + }) + if err != nil { + t.Fatal(err) + } + if len(cols) != 3 { + t.Fatal("cols should have 3 results", cols) + } + if cols[0][0] != 0 { + t.Fatal("cols[0][0] should map to id field in testUser", cols) + } + if cols[1][0] != 3 { + t.Fatal("cols[1][0] should map to email field in testUser", cols) + } + if cols[2][0] != 4 { + t.Fatal("cols[2][0] should map to created_at field in testUser", cols) + } +}