Skip to content

Commit 7f030ea

Browse files
committed
Initialise plans using sync.Once
Previously, plans were initialised through a racy assignment. Now they are initialised through a sync.Once, which is more clearly race free and also makes the intent clear (it is initialisation). With this change it's also necessary to make the plan more pointer-like, since if you pass a struct around by value with a lock in it, you are going to have a bad time. This was pointed out to me by the gometalinter. Fixes #296. Supercedes #153.
1 parent 75d359a commit 7f030ea

1 file changed

Lines changed: 16 additions & 21 deletions

File tree

table_bindings.go

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"bytes"
1616
"fmt"
1717
"reflect"
18+
"sync"
1819
)
1920

2021
// CustomScanner binds a database column value to a Go type
@@ -44,9 +45,10 @@ type bindPlan struct {
4445
versField string
4546
autoIncrIdx int
4647
autoIncrFieldName string
48+
once sync.Once
4749
}
4850

49-
func (plan bindPlan) createBindInstance(elem reflect.Value, conv TypeConverter) (bindInstance, error) {
51+
func (plan *bindPlan) createBindInstance(elem reflect.Value, conv TypeConverter) (bindInstance, error) {
5052
bi := bindInstance{query: plan.query, autoIncrIdx: plan.autoIncrIdx, autoIncrFieldName: plan.autoIncrFieldName, versField: plan.versField}
5153
if plan.versField != "" {
5254
bi.existingVersion = elem.FieldByName(plan.versField).Int()
@@ -100,8 +102,8 @@ type bindInstance struct {
100102
}
101103

102104
func (t *TableMap) bindInsert(elem reflect.Value) (bindInstance, error) {
103-
plan := t.insertPlan
104-
if plan.query == "" {
105+
plan := &t.insertPlan
106+
plan.once.Do(func() {
105107
plan.autoIncrIdx = -1
106108

107109
s := bytes.Buffer{}
@@ -154,16 +156,14 @@ func (t *TableMap) bindInsert(elem reflect.Value) (bindInstance, error) {
154156
s.WriteString(t.dbmap.Dialect.QuerySuffix())
155157

156158
plan.query = s.String()
157-
t.insertPlan = plan
158-
}
159+
})
159160

160161
return plan.createBindInstance(elem, t.dbmap.TypeConverter)
161162
}
162163

163164
func (t *TableMap) bindUpdate(elem reflect.Value) (bindInstance, error) {
164-
plan := t.updatePlan
165-
if plan.query == "" {
166-
165+
plan := &t.updatePlan
166+
plan.once.Do(func() {
167167
s := bytes.Buffer{}
168168
s.WriteString(fmt.Sprintf("update %s set ", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
169169
x := 0
@@ -212,16 +212,14 @@ func (t *TableMap) bindUpdate(elem reflect.Value) (bindInstance, error) {
212212
s.WriteString(t.dbmap.Dialect.QuerySuffix())
213213

214214
plan.query = s.String()
215-
t.updatePlan = plan
216-
}
215+
})
217216

218217
return plan.createBindInstance(elem, t.dbmap.TypeConverter)
219218
}
220219

221220
func (t *TableMap) bindDelete(elem reflect.Value) (bindInstance, error) {
222-
plan := t.deletePlan
223-
if plan.query == "" {
224-
221+
plan := &t.deletePlan
222+
plan.once.Do(func() {
225223
s := bytes.Buffer{}
226224
s.WriteString(fmt.Sprintf("delete from %s", t.dbmap.Dialect.QuotedTableForQuery(t.SchemaName, t.TableName)))
227225

@@ -258,16 +256,14 @@ func (t *TableMap) bindDelete(elem reflect.Value) (bindInstance, error) {
258256
s.WriteString(t.dbmap.Dialect.QuerySuffix())
259257

260258
plan.query = s.String()
261-
t.deletePlan = plan
262-
}
259+
})
263260

264261
return plan.createBindInstance(elem, t.dbmap.TypeConverter)
265262
}
266263

267-
func (t *TableMap) bindGet() bindPlan {
268-
plan := t.getPlan
269-
if plan.query == "" {
270-
264+
func (t *TableMap) bindGet() *bindPlan {
265+
plan := &t.getPlan
266+
plan.once.Do(func() {
271267
s := bytes.Buffer{}
272268
s.WriteString("select ")
273269

@@ -299,8 +295,7 @@ func (t *TableMap) bindGet() bindPlan {
299295
s.WriteString(t.dbmap.Dialect.QuerySuffix())
300296

301297
plan.query = s.String()
302-
t.getPlan = plan
303-
}
298+
})
304299

305300
return plan
306301
}

0 commit comments

Comments
 (0)