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
17 changes: 17 additions & 0 deletions reflectx/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type Mapper struct {
tagName string
tagMapFunc func(string) string
mapFunc func(string) string
colMapFunc func(string) string
mutex sync.Mutex
}

Expand All @@ -89,6 +90,19 @@ func NewMapperTagFunc(tagName string, mapFunc, tagMapFunc func(string) string) *
}
}

// NewMapperTagColFunc returns a new mapper which contains a mapper for field names
// AND a mapper for tag values AND sql column names. This is useful for tags like json which can
// have values like "name,omitempty", or on databases like Sqlite where column casing in queries cause problems.
func NewMapperTagColFunc(tagName string, mapFunc, tagMapFunc, colMapFunc func(string) string) *Mapper {
return &Mapper{
cache: make(map[reflect.Type]*StructMap),
tagName: tagName,
mapFunc: mapFunc,
tagMapFunc: tagMapFunc,
colMapFunc: colMapFunc,
}
}

// NewMapperFunc returns a new mapper which optionally obeys a field tag and
// a struct field name mapper func given by f. Tags will take precedence, but
// for any other field, the mapped name will be f(field.Name)
Expand Down Expand Up @@ -189,6 +203,9 @@ func (m *Mapper) TraversalsByNameFunc(t reflect.Type, names []string, fn func(in
nameCounter := make(map[string]int, len(names))

for i, name := range names {
if m.colMapFunc != nil {
name = m.colMapFunc(name)
}
fi, ok := tm.Names[name]
if !ok {
if leafs, lok := tm.Leafs[name]; lok {
Expand Down
20 changes: 19 additions & 1 deletion sqlx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ func TestNamedQuery(t *testing.T) {
t.Errorf("Expected LastName of `smith`, got `%s` (%s)", jp.LastName.String, db.DriverName())
}
if jp.Email.String != "ben@smith.com" {
t.Errorf("Expected first name of `doe`, got `%s` (%s)", jp.Email.String, db.DriverName())
t.Errorf("Expected email of `ben@smith.com`, got `%s` (%s)", jp.Email.String, db.DriverName())
}
}
}
Expand Down Expand Up @@ -852,6 +852,24 @@ func TestNamedQuery(t *testing.T) {

check(t, rows)

// Test the new col mapper which maps the sql column names also.
db.Mapper = reflectx.NewMapperTagColFunc("json", strings.ToLower, strings.ToLower, strings.ToLower)

// Sqlite will return the column names with the same case as we wrote in the query.
// So in queries with mixed casing, it will cause problems.
rows, err = db.NamedQuery(pdb(`
SELECT "FIRST" as fIrSt, last_name as LAST_NAME, "EMAIL" as email FROM jsperson
WHERE
"FIRST"=:FIRST AND
last_name=:last_name AND
"EMAIL"=:EMAIL
`, db), jp)
if err != nil {
t.Fatal(err)
}

check(t, rows)

db.Mapper = old

// Test nested structs
Expand Down
Loading