Skip to content

Commit 28dd33a

Browse files
committed
1 parent c269bfa commit 28dd33a

4 files changed

Lines changed: 187 additions & 3 deletions

File tree

db.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"reflect"
1616
"strconv"
1717
"strings"
18+
"sync"
1819
"time"
1920
)
2021

@@ -92,6 +93,14 @@ type DbMap struct {
9293
tables []*TableMap
9394
logger GorpLogger
9495
logPrefix string
96+
lock sync.RWMutex
97+
98+
Cache Cache
99+
}
100+
101+
type Cache interface {
102+
Load(key interface{}) (value interface{}, ok bool)
103+
Store(key, value interface{})
95104
}
96105

97106
func (m *DbMap) WithContext(ctx context.Context) SqlExecutor {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/mmrath/gorp
22

3-
go 1.13
3+
go 1.14
44

55
require (
66
github.com/go-sql-driver/mysql v1.4.1

gorp.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"reflect"
1313
"regexp"
1414
"strings"
15+
"sync"
1516
"time"
1617
)
1718

@@ -247,7 +248,37 @@ func expandNamedQuery(m *DbMap, query string, keyGetter func(key string) reflect
247248
}), args
248249
}
249250

251+
type fieldCacheKey struct {
252+
t reflect.Type
253+
name string
254+
cols string
255+
}
256+
257+
type fieldCacheEntry struct {
258+
mapping [][]int
259+
err error
260+
}
261+
250262
func columnToFieldIndex(m *DbMap, t reflect.Type, cols []string) ([][]int, error) {
263+
var ck fieldCacheKey
264+
var err error
265+
if m.Cache != nil {
266+
ck.t = t
267+
ck.cols = strings.Join(cols, ",")
268+
269+
rv, ok := m.Cache.Load(ck)
270+
if ok {
271+
entry := rv.(*fieldCacheEntry)
272+
return entry.mapping, entry.err
273+
}
274+
} else {
275+
m.lock.Lock()
276+
if m.Cache == nil {
277+
m.Cache = &sync.Map{}
278+
}
279+
m.lock.Unlock()
280+
}
281+
251282
colToFieldIndex := make([][]int, len(cols))
252283

253284
// check if type t is a mapped table - if so we'll
@@ -289,13 +320,20 @@ func columnToFieldIndex(m *DbMap, t reflect.Type, cols []string) ([][]int, error
289320
missingColNames = append(missingColNames, colName)
290321
}
291322
}
323+
292324
if len(missingColNames) > 0 {
293-
return colToFieldIndex, &NoFieldInTypeError{
325+
err = &NoFieldInTypeError{
294326
TypeName: t.Name(),
295327
MissingColNames: missingColNames,
296328
}
297329
}
298-
return colToFieldIndex, nil
330+
entry := &fieldCacheEntry{
331+
mapping: colToFieldIndex,
332+
err: err,
333+
}
334+
m.Cache.Store(ck, entry)
335+
336+
return colToFieldIndex, err
299337
}
300338

301339
// toSliceType returns the element type of the given object, if the object is a

gorp_mapping_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//Original: https://github.com/paulquerna-okta/gorp/blob/1fb48e4c1f26abac8d69b7b2cca5aeafde0c3532/mapping_test.go
2+
3+
package gorp
4+
5+
import (
6+
"reflect"
7+
"sync"
8+
"testing"
9+
"time"
10+
)
11+
12+
type testUser struct {
13+
ID uint64 `db:"id"`
14+
Username string `db:"user_name"`
15+
HashedPassword []byte `db:"hashed_password"`
16+
EMail string `db:"email"`
17+
CreatedAt time.Time `db:"created_at"`
18+
UpdatedAt time.Time `db:"updated_at"`
19+
}
20+
21+
type testCoolUser struct {
22+
testUser
23+
IsCool bool `db:"is_cool"`
24+
BestFriends []string `db:"best_friends"`
25+
}
26+
27+
func BenchmarkColumnToFieldIndex(b *testing.B) {
28+
structType := reflect.TypeOf(testUser{})
29+
dbmap := &DbMap{Cache: &sync.Map{}}
30+
b.ResetTimer()
31+
for n := 0; n < b.N; n++ {
32+
_, err := columnToFieldIndex(dbmap,
33+
structType,
34+
[]string{
35+
"user_name",
36+
"email",
37+
"created_at",
38+
"updated_at",
39+
"id",
40+
})
41+
if err != nil {
42+
panic(err)
43+
}
44+
}
45+
}
46+
47+
func TestColumnToFieldIndexBasic(t *testing.T) {
48+
structType := reflect.TypeOf(testUser{})
49+
dbmap := &DbMap{}
50+
cols, err := columnToFieldIndex(dbmap,
51+
structType,
52+
[]string{
53+
"email",
54+
})
55+
if err != nil {
56+
t.Fatal(err)
57+
}
58+
if len(cols) != 1 {
59+
t.Fatal("cols should have 1 result", cols)
60+
}
61+
if cols[0][0] != 3 {
62+
t.Fatal("cols[0][0] should map to email field in testUser", cols)
63+
}
64+
}
65+
66+
func TestColumnToFieldIndexSome(t *testing.T) {
67+
structType := reflect.TypeOf(testUser{})
68+
dbmap := &DbMap{}
69+
cols, err := columnToFieldIndex(dbmap,
70+
structType,
71+
[]string{
72+
"id",
73+
"email",
74+
"created_at",
75+
})
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
if len(cols) != 3 {
80+
t.Fatal("cols should have 3 results", cols)
81+
}
82+
if cols[0][0] != 0 {
83+
t.Fatal("cols[0][0] should map to id field in testUser", cols)
84+
}
85+
if cols[1][0] != 3 {
86+
t.Fatal("cols[1][0] should map to email field in testUser", cols)
87+
}
88+
if cols[2][0] != 4 {
89+
t.Fatal("cols[2][0] should map to created_at field in testUser", cols)
90+
}
91+
}
92+
93+
func TestColumnToFieldIndexEmbedded(t *testing.T) {
94+
structType := reflect.TypeOf(testCoolUser{})
95+
dbmap := &DbMap{}
96+
cols, err := columnToFieldIndex(dbmap,
97+
structType,
98+
[]string{
99+
"id",
100+
"email",
101+
"is_cool",
102+
})
103+
if err != nil {
104+
t.Fatal(err)
105+
}
106+
if len(cols) != 3 {
107+
t.Fatal("cols should have 3 results", cols)
108+
}
109+
if cols[0][0] != 0 && cols[0][1] != 0 {
110+
t.Fatal("cols[0][0] should map to id field in testCoolUser", cols)
111+
}
112+
if cols[1][0] != 0 && cols[1][1] != 3 {
113+
t.Fatal("cols[1][0] should map to email field in testCoolUser", cols)
114+
}
115+
if cols[2][0] != 1 {
116+
t.Fatal("cols[2][0] should map to is_cool field in testCoolUser", cols)
117+
}
118+
}
119+
120+
func TestColumnToFieldIndexEmbeddedFriends(t *testing.T) {
121+
structType := reflect.TypeOf(testCoolUser{})
122+
dbmap := &DbMap{}
123+
cols, err := columnToFieldIndex(dbmap,
124+
structType,
125+
[]string{
126+
"best_friends",
127+
})
128+
if err != nil {
129+
t.Fatal(err)
130+
}
131+
if len(cols) != 1 {
132+
t.Fatal("cols should have 1 results", cols)
133+
}
134+
if cols[0][0] != 2 {
135+
t.Fatal("cols[0][0] should map to BestFriends field in testCoolUser", cols)
136+
}
137+
}

0 commit comments

Comments
 (0)