Skip to content

Commit 48e25ac

Browse files
Minnowomrj0
authored andcommitted
add mapper that applies to sql columns
1 parent f10f9d2 commit 48e25ac

2 files changed

Lines changed: 36 additions & 1 deletion

File tree

reflectx/reflect.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ type Mapper struct {
6565
tagName string
6666
tagMapFunc func(string) string
6767
mapFunc func(string) string
68+
colMapFunc func(string) string
6869
mutex sync.Mutex
6970
}
7071

@@ -89,6 +90,19 @@ func NewMapperTagFunc(tagName string, mapFunc, tagMapFunc func(string) string) *
8990
}
9091
}
9192

93+
// NewMapperTagColFunc returns a new mapper which contains a mapper for field names
94+
// AND a mapper for tag values AND sql column names. This is useful for tags like json which can
95+
// have values like "name,omitempty", or on databases like Sqlite where column casing in queries cause problems.
96+
func NewMapperTagColFunc(tagName string, mapFunc, tagMapFunc, colMapFunc func(string) string) *Mapper {
97+
return &Mapper{
98+
cache: make(map[reflect.Type]*StructMap),
99+
tagName: tagName,
100+
mapFunc: mapFunc,
101+
tagMapFunc: tagMapFunc,
102+
colMapFunc: colMapFunc,
103+
}
104+
}
105+
92106
// NewMapperFunc returns a new mapper which optionally obeys a field tag and
93107
// a struct field name mapper func given by f. Tags will take precedence, but
94108
// for any other field, the mapped name will be f(field.Name)
@@ -189,6 +203,9 @@ func (m *Mapper) TraversalsByNameFunc(t reflect.Type, names []string, fn func(in
189203
nameCounter := make(map[string]int, len(names))
190204

191205
for i, name := range names {
206+
if m.colMapFunc != nil {
207+
name = m.colMapFunc(name)
208+
}
192209
fi, ok := tm.Names[name]
193210
if !ok {
194211
if leafs, lok := tm.Leafs[name]; lok {

sqlx_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ func TestNamedQuery(t *testing.T) {
815815
t.Errorf("Expected LastName of `smith`, got `%s` (%s)", jp.LastName.String, db.DriverName())
816816
}
817817
if jp.Email.String != "ben@smith.com" {
818-
t.Errorf("Expected first name of `doe`, got `%s` (%s)", jp.Email.String, db.DriverName())
818+
t.Errorf("Expected email of `ben@smith.com`, got `%s` (%s)", jp.Email.String, db.DriverName())
819819
}
820820
}
821821
}
@@ -852,6 +852,24 @@ func TestNamedQuery(t *testing.T) {
852852

853853
check(t, rows)
854854

855+
// Test the new col mapper which maps the sql column names also.
856+
db.Mapper = reflectx.NewMapperTagColFunc("json", strings.ToLower, strings.ToLower, strings.ToLower)
857+
858+
// Sqlite will return the column names with the same case as we wrote in the query.
859+
// So in queries with mixed casing, it will cause problems.
860+
rows, err = db.NamedQuery(pdb(`
861+
SELECT "FIRST" as fIrSt, last_name as LAST_NAME, "EMAIL" as email FROM jsperson
862+
WHERE
863+
"FIRST"=:FIRST AND
864+
last_name=:last_name AND
865+
"EMAIL"=:EMAIL
866+
`, db), jp)
867+
if err != nil {
868+
t.Fatal(err)
869+
}
870+
871+
check(t, rows)
872+
855873
db.Mapper = old
856874

857875
// Test nested structs

0 commit comments

Comments
 (0)