Skip to content

Commit 51bcb84

Browse files
Deizdoug-martin
authored andcommitted
feat: add SetDefaultPrepared which controls query value interpolation
This switches the datasets' internal isPrepared bools into a custom type that resolves back into a bool when passed into the SQL builder. This could have also been a *bool but I wanted to avoid nil checks and potential sources of confusion if e.g. a child dataset happened to dereference and mutate the pointer's value.
1 parent 6c631e9 commit 51bcb84

11 files changed

+139
-24
lines changed

delete_dataset.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a
1212
type DeleteDataset struct {
1313
dialect SQLDialect
1414
clauses exp.DeleteClauses
15-
isPrepared bool
15+
isPrepared prepared
1616
queryFactory exec.QueryFactory
1717
err error
1818
}
@@ -23,7 +23,7 @@ func newDeleteDataset(d string, queryFactory exec.QueryFactory) *DeleteDataset {
2323
clauses: exp.NewDeleteClauses(),
2424
dialect: GetDialect(d),
2525
queryFactory: queryFactory,
26-
isPrepared: false,
26+
isPrepared: preparedNoPreference,
2727
err: nil,
2828
}
2929
}
@@ -46,13 +46,13 @@ func (dd *DeleteDataset) Clone() exp.Expression {
4646
// prepared: If true the dataset WILL NOT interpolate the parameters.
4747
func (dd *DeleteDataset) Prepared(prepared bool) *DeleteDataset {
4848
ret := dd.copy(dd.clauses)
49-
ret.isPrepared = prepared
49+
ret.isPrepared = preparedFromBool(prepared)
5050
return ret
5151
}
5252

5353
// Returns true if Prepared(true) has been called on this dataset
5454
func (dd *DeleteDataset) IsPrepared() bool {
55-
return dd.isPrepared
55+
return dd.isPrepared.Bool()
5656
}
5757

5858
// Sets the adapter used to serialize values and create the SQL statement
@@ -235,7 +235,7 @@ func (dd *DeleteDataset) Executor() exec.QueryExecutor {
235235
}
236236

237237
func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder {
238-
buf := sb.NewSQLBuilder(dd.isPrepared)
238+
buf := sb.NewSQLBuilder(dd.isPrepared.Bool())
239239
if dd.err != nil {
240240
return buf.SetError(dd.err)
241241
}

delete_dataset_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ func (dds *deleteDatasetSuite) TestPrepared() {
8989
dds.False(ds.IsPrepared())
9090
// should apply the prepared to any datasets created from the root
9191
dds.True(preparedDs.Where(goqu.Ex{"a": 1}).IsPrepared())
92+
93+
defer goqu.SetDefaultPrepared(false)
94+
goqu.SetDefaultPrepared(true)
95+
96+
// should be prepared by default
97+
ds = goqu.Delete("test")
98+
dds.True(ds.IsPrepared())
9299
}
93100

94101
func (dds *deleteDatasetSuite) TestGetClauses() {
@@ -445,6 +452,14 @@ func (dds *deleteDatasetSuite) TestExecutor() {
445452
dds.NoError(err)
446453
dds.Equal([]interface{}{int64(10)}, args)
447454
dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql)
455+
456+
defer goqu.SetDefaultPrepared(false)
457+
goqu.SetDefaultPrepared(true)
458+
459+
dsql, args, err = ds.Executor().ToSQL()
460+
dds.NoError(err)
461+
dds.Equal([]interface{}{int64(10)}, args)
462+
dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql)
448463
}
449464

450465
func (dds *deleteDatasetSuite) TestSetError() {

insert_dataset.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
type InsertDataset struct {
1313
dialect SQLDialect
1414
clauses exp.InsertClauses
15-
isPrepared bool
15+
isPrepared prepared
1616
queryFactory exec.QueryFactory
1717
err error
1818
}
@@ -39,12 +39,12 @@ func Insert(table interface{}) *InsertDataset {
3939
// prepared: If true the dataset WILL NOT interpolate the parameters.
4040
func (id *InsertDataset) Prepared(prepared bool) *InsertDataset {
4141
ret := id.copy(id.clauses)
42-
ret.isPrepared = prepared
42+
ret.isPrepared = preparedFromBool(prepared)
4343
return ret
4444
}
4545

4646
func (id *InsertDataset) IsPrepared() bool {
47-
return id.isPrepared
47+
return id.isPrepared.Bool()
4848
}
4949

5050
// Sets the adapter used to serialize values and create the SQL statement
@@ -257,7 +257,7 @@ func (id *InsertDataset) Executor() exec.QueryExecutor {
257257
}
258258

259259
func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder {
260-
buf := sb.NewSQLBuilder(id.isPrepared)
260+
buf := sb.NewSQLBuilder(id.isPrepared.Bool())
261261
if id.err != nil {
262262
return buf.SetError(id.err)
263263
}

insert_dataset_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ func (ids *insertDatasetSuite) TestPrepared() {
7070
ids.False(ds.IsPrepared())
7171
// should apply the prepared to any datasets created from the root
7272
ids.True(preparedDs.Returning(goqu.C("col")).IsPrepared())
73+
74+
defer goqu.SetDefaultPrepared(false)
75+
goqu.SetDefaultPrepared(true)
76+
77+
// should be prepared by default
78+
ds = goqu.Insert("test")
79+
ids.True(ds.IsPrepared())
7380
}
7481

7582
func (ids *insertDatasetSuite) TestGetClauses() {
@@ -440,6 +447,14 @@ func (ids *insertDatasetSuite) TestExecutor() {
440447
ids.NoError(err)
441448
ids.Equal([]interface{}{"111 Test Addr", "Test1"}, args)
442449
ids.Equal(`INSERT INTO "items" ("address", "name") VALUES (?, ?)`, isql)
450+
451+
defer goqu.SetDefaultPrepared(false)
452+
goqu.SetDefaultPrepared(true)
453+
454+
isql, args, err = ds.Executor().ToSQL()
455+
ids.NoError(err)
456+
ids.Equal([]interface{}{"111 Test Addr", "Test1"}, args)
457+
ids.Equal(`INSERT INTO "items" ("address", "name") VALUES (?, ?)`, isql)
443458
}
444459

445460
func (ids *insertDatasetSuite) TestInsertStruct() {

prepared.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package goqu
2+
3+
var (
4+
// defaultPrepared is controlled by SetDefaultPrepared
5+
defaultPrepared bool
6+
)
7+
8+
type prepared int
9+
10+
const (
11+
// zero value that defers to defaultPrepared
12+
preparedNoPreference prepared = iota
13+
14+
// explicitly enabled via Prepared(true) on a dataset
15+
preparedEnabled
16+
17+
// explicitly disabled via Prepared(false) on a dataset
18+
preparedDisabled
19+
)
20+
21+
// Bool converts the ternary prepared state into a boolean. If the prepared
22+
// state is preparedNoPreference, the value depends on the last value that
23+
// SetDefaultPrepared was called with which is false by default.
24+
func (p prepared) Bool() bool {
25+
if p == preparedNoPreference {
26+
return defaultPrepared
27+
} else if p == preparedEnabled {
28+
return true
29+
}
30+
31+
return false
32+
}
33+
34+
// preparedFromBool converts a bool from e.g. Prepared(true) into a prepared
35+
// const.
36+
func preparedFromBool(prepared bool) prepared {
37+
if prepared {
38+
return preparedEnabled
39+
}
40+
41+
return preparedDisabled
42+
}
43+
44+
// SetDefaultPrepared controls the default Prepared state of all datasets. If
45+
// set to true, any new dataset will use prepared queries by default.
46+
func SetDefaultPrepared(prepared bool) {
47+
defaultPrepared = prepared
48+
}

select_dataset.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
type SelectDataset struct {
1515
dialect SQLDialect
1616
clauses exp.SelectClauses
17-
isPrepared bool
17+
isPrepared prepared
1818
queryFactory exec.QueryFactory
1919
err error
2020
}
@@ -52,12 +52,12 @@ func (sd *SelectDataset) WithDialect(dl string) *SelectDataset {
5252
// prepared: If true the dataset WILL NOT interpolate the parameters.
5353
func (sd *SelectDataset) Prepared(prepared bool) *SelectDataset {
5454
ret := sd.copy(sd.clauses)
55-
ret.isPrepared = prepared
55+
ret.isPrepared = preparedFromBool(prepared)
5656
return ret
5757
}
5858

5959
func (sd *SelectDataset) IsPrepared() bool {
60-
return sd.isPrepared
60+
return sd.isPrepared.Bool()
6161
}
6262

6363
// Returns the current adapter on the dataset
@@ -101,7 +101,7 @@ func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
101101
// `ORDER , and `LIMIT`
102102
func (sd *SelectDataset) Update() *UpdateDataset {
103103
u := newUpdateDataset(sd.dialect.Dialect(), sd.queryFactory).
104-
Prepared(sd.isPrepared)
104+
Prepared(sd.isPrepared.Bool())
105105
if sd.clauses.HasSources() {
106106
u = u.Table(sd.GetClauses().From().Columns()[0])
107107
}
@@ -128,7 +128,7 @@ func (sd *SelectDataset) Update() *UpdateDataset {
128128
// insert.
129129
func (sd *SelectDataset) Insert() *InsertDataset {
130130
i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory).
131-
Prepared(sd.isPrepared)
131+
Prepared(sd.isPrepared.Bool())
132132
if sd.clauses.HasSources() {
133133
i = i.Into(sd.GetClauses().From().Columns()[0])
134134
}
@@ -144,7 +144,7 @@ func (sd *SelectDataset) Insert() *InsertDataset {
144144
// `ORDER , and `LIMIT`
145145
func (sd *SelectDataset) Delete() *DeleteDataset {
146146
d := newDeleteDataset(sd.dialect.Dialect(), sd.queryFactory).
147-
Prepared(sd.isPrepared)
147+
Prepared(sd.isPrepared.Bool())
148148
if sd.clauses.HasSources() {
149149
d = d.From(sd.clauses.From().Columns()[0])
150150
}
@@ -686,7 +686,7 @@ func (sd *SelectDataset) PluckContext(ctx context.Context, i interface{}, col st
686686
}
687687

688688
func (sd *SelectDataset) selectSQLBuilder() sb.SQLBuilder {
689-
buf := sb.NewSQLBuilder(sd.isPrepared)
689+
buf := sb.NewSQLBuilder(sd.isPrepared.Bool())
690690
if sd.err != nil {
691691
return buf.SetError(sd.err)
692692
}

select_dataset_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ func (sds *selectDatasetSuite) TestPrepared() {
7676
sds.False(ds.IsPrepared())
7777
// should apply the prepared to any datasets created from the root
7878
sds.True(preparedDs.Where(goqu.Ex{"a": 1}).IsPrepared())
79+
80+
defer goqu.SetDefaultPrepared(false)
81+
goqu.SetDefaultPrepared(true)
82+
83+
// should be prepared by default
84+
ds = goqu.From("test")
85+
sds.True(ds.IsPrepared())
7986
}
8087

8188
func (sds *selectDatasetSuite) TestGetClauses() {

truncate_dataset.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
type TruncateDataset struct {
1010
dialect SQLDialect
1111
clauses exp.TruncateClauses
12-
isPrepared bool
12+
isPrepared prepared
1313
queryFactory exec.QueryFactory
1414
err error
1515
}
@@ -39,12 +39,12 @@ func (td *TruncateDataset) WithDialect(dl string) *TruncateDataset {
3939
// prepared: If true the dataset WILL NOT interpolate the parameters.
4040
func (td *TruncateDataset) Prepared(prepared bool) *TruncateDataset {
4141
ret := td.copy(td.clauses)
42-
ret.isPrepared = prepared
42+
ret.isPrepared = preparedFromBool(prepared)
4343
return ret
4444
}
4545

4646
func (td *TruncateDataset) IsPrepared() bool {
47-
return td.isPrepared
47+
return td.isPrepared.Bool()
4848
}
4949

5050
// Returns the current adapter on the dataset
@@ -160,7 +160,7 @@ func (td *TruncateDataset) Executor() exec.QueryExecutor {
160160
}
161161

162162
func (td *TruncateDataset) truncateSQLBuilder() sb.SQLBuilder {
163-
buf := sb.NewSQLBuilder(td.isPrepared)
163+
buf := sb.NewSQLBuilder(td.isPrepared.Bool())
164164
if td.err != nil {
165165
return buf.SetError(td.err)
166166
}

truncate_dataset_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ func (tds *truncateDatasetSuite) TestPrepared() {
6262
tds.False(ds.IsPrepared())
6363
// should apply the prepared to any datasets created from the root
6464
tds.True(preparedDs.Restrict().IsPrepared())
65+
66+
defer goqu.SetDefaultPrepared(false)
67+
goqu.SetDefaultPrepared(true)
68+
69+
// should be prepared by default
70+
ds = goqu.Truncate("test")
71+
tds.True(ds.IsPrepared())
6572
}
6673

6774
func (tds *truncateDatasetSuite) TestGetClauses() {
@@ -274,6 +281,14 @@ func (tds *truncateDatasetSuite) TestExecutor() {
274281
tds.NoError(err)
275282
tds.Empty(args)
276283
tds.Equal(`TRUNCATE "table1", "table2"`, tsql)
284+
285+
defer goqu.SetDefaultPrepared(false)
286+
goqu.SetDefaultPrepared(true)
287+
288+
tsql, args, err = ds.Executor().ToSQL()
289+
tds.NoError(err)
290+
tds.Empty(args)
291+
tds.Equal(`TRUNCATE "table1", "table2"`, tsql)
277292
}
278293

279294
func (tds *truncateDatasetSuite) TestSetError() {

update_dataset.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
type UpdateDataset struct {
1111
dialect SQLDialect
1212
clauses exp.UpdateClauses
13-
isPrepared bool
13+
isPrepared prepared
1414
queryFactory exec.QueryFactory
1515
err error
1616
}
@@ -35,12 +35,12 @@ func Update(table interface{}) *UpdateDataset {
3535
// prepared: If true the dataset WILL NOT interpolate the parameters.
3636
func (ud *UpdateDataset) Prepared(prepared bool) *UpdateDataset {
3737
ret := ud.copy(ud.clauses)
38-
ret.isPrepared = prepared
38+
ret.isPrepared = preparedFromBool(prepared)
3939
return ret
4040
}
4141

4242
func (ud *UpdateDataset) IsPrepared() bool {
43-
return ud.isPrepared
43+
return ud.isPrepared.Bool()
4444
}
4545

4646
// Sets the adapter used to serialize values and create the SQL statement
@@ -236,7 +236,7 @@ func (ud *UpdateDataset) Executor() exec.QueryExecutor {
236236
}
237237

238238
func (ud *UpdateDataset) updateSQLBuilder() sb.SQLBuilder {
239-
buf := sb.NewSQLBuilder(ud.isPrepared)
239+
buf := sb.NewSQLBuilder(ud.isPrepared.Bool())
240240
if ud.err != nil {
241241
return buf.SetError(ud.err)
242242
}

0 commit comments

Comments
 (0)