diff --git a/delete_dataset.go b/delete_dataset.go index 3115a72f..320925a9 100644 --- a/delete_dataset.go +++ b/delete_dataset.go @@ -12,7 +12,7 @@ var ErrBadFromArgument = errors.New("unsupported DeleteDataset#From argument, a type DeleteDataset struct { dialect SQLDialect clauses exp.DeleteClauses - isPrepared bool + isPrepared prepared queryFactory exec.QueryFactory err error } @@ -23,7 +23,7 @@ func newDeleteDataset(d string, queryFactory exec.QueryFactory) *DeleteDataset { clauses: exp.NewDeleteClauses(), dialect: GetDialect(d), queryFactory: queryFactory, - isPrepared: false, + isPrepared: preparedNoPreference, err: nil, } } @@ -46,13 +46,13 @@ func (dd *DeleteDataset) Clone() exp.Expression { // prepared: If true the dataset WILL NOT interpolate the parameters. func (dd *DeleteDataset) Prepared(prepared bool) *DeleteDataset { ret := dd.copy(dd.clauses) - ret.isPrepared = prepared + ret.isPrepared = preparedFromBool(prepared) return ret } // Returns true if Prepared(true) has been called on this dataset func (dd *DeleteDataset) IsPrepared() bool { - return dd.isPrepared + return dd.isPrepared.Bool() } // Sets the adapter used to serialize values and create the SQL statement @@ -235,7 +235,7 @@ func (dd *DeleteDataset) Executor() exec.QueryExecutor { } func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder { - buf := sb.NewSQLBuilder(dd.isPrepared) + buf := sb.NewSQLBuilder(dd.isPrepared.Bool()) if dd.err != nil { return buf.SetError(dd.err) } diff --git a/delete_dataset_test.go b/delete_dataset_test.go index d0a4d3be..e98cf4f6 100644 --- a/delete_dataset_test.go +++ b/delete_dataset_test.go @@ -89,6 +89,13 @@ func (dds *deleteDatasetSuite) TestPrepared() { dds.False(ds.IsPrepared()) // should apply the prepared to any datasets created from the root dds.True(preparedDs.Where(goqu.Ex{"a": 1}).IsPrepared()) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + // should be prepared by default + ds = goqu.Delete("test") + dds.True(ds.IsPrepared()) } func (dds *deleteDatasetSuite) TestGetClauses() { @@ -445,6 +452,14 @@ func (dds *deleteDatasetSuite) TestExecutor() { dds.NoError(err) dds.Equal([]interface{}{int64(10)}, args) dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + dsql, args, err = ds.Executor().ToSQL() + dds.NoError(err) + dds.Equal([]interface{}{int64(10)}, args) + dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql) } func (dds *deleteDatasetSuite) TestSetError() { diff --git a/insert_dataset.go b/insert_dataset.go index ba474813..3ed270db 100644 --- a/insert_dataset.go +++ b/insert_dataset.go @@ -12,7 +12,7 @@ import ( type InsertDataset struct { dialect SQLDialect clauses exp.InsertClauses - isPrepared bool + isPrepared prepared queryFactory exec.QueryFactory err error } @@ -39,12 +39,12 @@ func Insert(table interface{}) *InsertDataset { // prepared: If true the dataset WILL NOT interpolate the parameters. func (id *InsertDataset) Prepared(prepared bool) *InsertDataset { ret := id.copy(id.clauses) - ret.isPrepared = prepared + ret.isPrepared = preparedFromBool(prepared) return ret } func (id *InsertDataset) IsPrepared() bool { - return id.isPrepared + return id.isPrepared.Bool() } // Sets the adapter used to serialize values and create the SQL statement @@ -257,7 +257,7 @@ func (id *InsertDataset) Executor() exec.QueryExecutor { } func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder { - buf := sb.NewSQLBuilder(id.isPrepared) + buf := sb.NewSQLBuilder(id.isPrepared.Bool()) if id.err != nil { return buf.SetError(id.err) } diff --git a/insert_dataset_test.go b/insert_dataset_test.go index 7e3492a5..b7e671b7 100644 --- a/insert_dataset_test.go +++ b/insert_dataset_test.go @@ -70,6 +70,13 @@ func (ids *insertDatasetSuite) TestPrepared() { ids.False(ds.IsPrepared()) // should apply the prepared to any datasets created from the root ids.True(preparedDs.Returning(goqu.C("col")).IsPrepared()) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + // should be prepared by default + ds = goqu.Insert("test") + ids.True(ds.IsPrepared()) } func (ids *insertDatasetSuite) TestGetClauses() { @@ -440,6 +447,14 @@ func (ids *insertDatasetSuite) TestExecutor() { ids.NoError(err) ids.Equal([]interface{}{"111 Test Addr", "Test1"}, args) ids.Equal(`INSERT INTO "items" ("address", "name") VALUES (?, ?)`, isql) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + isql, args, err = ds.Executor().ToSQL() + ids.NoError(err) + ids.Equal([]interface{}{"111 Test Addr", "Test1"}, args) + ids.Equal(`INSERT INTO "items" ("address", "name") VALUES (?, ?)`, isql) } func (ids *insertDatasetSuite) TestInsertStruct() { diff --git a/prepared.go b/prepared.go new file mode 100644 index 00000000..796ce98e --- /dev/null +++ b/prepared.go @@ -0,0 +1,48 @@ +package goqu + +var ( + // defaultPrepared is controlled by SetDefaultPrepared + defaultPrepared bool +) + +type prepared int + +const ( + // zero value that defers to defaultPrepared + preparedNoPreference prepared = iota + + // explicitly enabled via Prepared(true) on a dataset + preparedEnabled + + // explicitly disabled via Prepared(false) on a dataset + preparedDisabled +) + +// Bool converts the ternary prepared state into a boolean. If the prepared +// state is preparedNoPreference, the value depends on the last value that +// SetDefaultPrepared was called with which is false by default. +func (p prepared) Bool() bool { + if p == preparedNoPreference { + return defaultPrepared + } else if p == preparedEnabled { + return true + } + + return false +} + +// preparedFromBool converts a bool from e.g. Prepared(true) into a prepared +// const. +func preparedFromBool(prepared bool) prepared { + if prepared { + return preparedEnabled + } + + return preparedDisabled +} + +// SetDefaultPrepared controls the default Prepared state of all datasets. If +// set to true, any new dataset will use prepared queries by default. +func SetDefaultPrepared(prepared bool) { + defaultPrepared = prepared +} diff --git a/select_dataset.go b/select_dataset.go index 3b280c6c..d027a372 100644 --- a/select_dataset.go +++ b/select_dataset.go @@ -14,7 +14,7 @@ import ( type SelectDataset struct { dialect SQLDialect clauses exp.SelectClauses - isPrepared bool + isPrepared prepared queryFactory exec.QueryFactory err error } @@ -52,12 +52,12 @@ func (sd *SelectDataset) WithDialect(dl string) *SelectDataset { // prepared: If true the dataset WILL NOT interpolate the parameters. func (sd *SelectDataset) Prepared(prepared bool) *SelectDataset { ret := sd.copy(sd.clauses) - ret.isPrepared = prepared + ret.isPrepared = preparedFromBool(prepared) return ret } func (sd *SelectDataset) IsPrepared() bool { - return sd.isPrepared + return sd.isPrepared.Bool() } // Returns the current adapter on the dataset @@ -101,7 +101,7 @@ func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset { // `ORDER , and `LIMIT` func (sd *SelectDataset) Update() *UpdateDataset { u := newUpdateDataset(sd.dialect.Dialect(), sd.queryFactory). - Prepared(sd.isPrepared) + Prepared(sd.isPrepared.Bool()) if sd.clauses.HasSources() { u = u.Table(sd.GetClauses().From().Columns()[0]) } @@ -128,7 +128,7 @@ func (sd *SelectDataset) Update() *UpdateDataset { // insert. func (sd *SelectDataset) Insert() *InsertDataset { i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory). - Prepared(sd.isPrepared) + Prepared(sd.isPrepared.Bool()) if sd.clauses.HasSources() { i = i.Into(sd.GetClauses().From().Columns()[0]) } @@ -144,7 +144,7 @@ func (sd *SelectDataset) Insert() *InsertDataset { // `ORDER , and `LIMIT` func (sd *SelectDataset) Delete() *DeleteDataset { d := newDeleteDataset(sd.dialect.Dialect(), sd.queryFactory). - Prepared(sd.isPrepared) + Prepared(sd.isPrepared.Bool()) if sd.clauses.HasSources() { d = d.From(sd.clauses.From().Columns()[0]) } @@ -686,7 +686,7 @@ func (sd *SelectDataset) PluckContext(ctx context.Context, i interface{}, col st } func (sd *SelectDataset) selectSQLBuilder() sb.SQLBuilder { - buf := sb.NewSQLBuilder(sd.isPrepared) + buf := sb.NewSQLBuilder(sd.isPrepared.Bool()) if sd.err != nil { return buf.SetError(sd.err) } diff --git a/select_dataset_test.go b/select_dataset_test.go index 9e39034d..63fdd997 100644 --- a/select_dataset_test.go +++ b/select_dataset_test.go @@ -76,6 +76,13 @@ func (sds *selectDatasetSuite) TestPrepared() { sds.False(ds.IsPrepared()) // should apply the prepared to any datasets created from the root sds.True(preparedDs.Where(goqu.Ex{"a": 1}).IsPrepared()) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + // should be prepared by default + ds = goqu.From("test") + sds.True(ds.IsPrepared()) } func (sds *selectDatasetSuite) TestGetClauses() { diff --git a/truncate_dataset.go b/truncate_dataset.go index e714f5b7..fda8196b 100644 --- a/truncate_dataset.go +++ b/truncate_dataset.go @@ -9,7 +9,7 @@ import ( type TruncateDataset struct { dialect SQLDialect clauses exp.TruncateClauses - isPrepared bool + isPrepared prepared queryFactory exec.QueryFactory err error } @@ -39,12 +39,12 @@ func (td *TruncateDataset) WithDialect(dl string) *TruncateDataset { // prepared: If true the dataset WILL NOT interpolate the parameters. func (td *TruncateDataset) Prepared(prepared bool) *TruncateDataset { ret := td.copy(td.clauses) - ret.isPrepared = prepared + ret.isPrepared = preparedFromBool(prepared) return ret } func (td *TruncateDataset) IsPrepared() bool { - return td.isPrepared + return td.isPrepared.Bool() } // Returns the current adapter on the dataset @@ -160,7 +160,7 @@ func (td *TruncateDataset) Executor() exec.QueryExecutor { } func (td *TruncateDataset) truncateSQLBuilder() sb.SQLBuilder { - buf := sb.NewSQLBuilder(td.isPrepared) + buf := sb.NewSQLBuilder(td.isPrepared.Bool()) if td.err != nil { return buf.SetError(td.err) } diff --git a/truncate_dataset_test.go b/truncate_dataset_test.go index 4acbbb93..592de656 100644 --- a/truncate_dataset_test.go +++ b/truncate_dataset_test.go @@ -62,6 +62,13 @@ func (tds *truncateDatasetSuite) TestPrepared() { tds.False(ds.IsPrepared()) // should apply the prepared to any datasets created from the root tds.True(preparedDs.Restrict().IsPrepared()) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + // should be prepared by default + ds = goqu.Truncate("test") + tds.True(ds.IsPrepared()) } func (tds *truncateDatasetSuite) TestGetClauses() { @@ -274,6 +281,14 @@ func (tds *truncateDatasetSuite) TestExecutor() { tds.NoError(err) tds.Empty(args) tds.Equal(`TRUNCATE "table1", "table2"`, tsql) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + tsql, args, err = ds.Executor().ToSQL() + tds.NoError(err) + tds.Empty(args) + tds.Equal(`TRUNCATE "table1", "table2"`, tsql) } func (tds *truncateDatasetSuite) TestSetError() { diff --git a/update_dataset.go b/update_dataset.go index e8e7221b..e2eaa53b 100644 --- a/update_dataset.go +++ b/update_dataset.go @@ -10,7 +10,7 @@ import ( type UpdateDataset struct { dialect SQLDialect clauses exp.UpdateClauses - isPrepared bool + isPrepared prepared queryFactory exec.QueryFactory err error } @@ -35,12 +35,12 @@ func Update(table interface{}) *UpdateDataset { // prepared: If true the dataset WILL NOT interpolate the parameters. func (ud *UpdateDataset) Prepared(prepared bool) *UpdateDataset { ret := ud.copy(ud.clauses) - ret.isPrepared = prepared + ret.isPrepared = preparedFromBool(prepared) return ret } func (ud *UpdateDataset) IsPrepared() bool { - return ud.isPrepared + return ud.isPrepared.Bool() } // Sets the adapter used to serialize values and create the SQL statement @@ -236,7 +236,7 @@ func (ud *UpdateDataset) Executor() exec.QueryExecutor { } func (ud *UpdateDataset) updateSQLBuilder() sb.SQLBuilder { - buf := sb.NewSQLBuilder(ud.isPrepared) + buf := sb.NewSQLBuilder(ud.isPrepared.Bool()) if ud.err != nil { return buf.SetError(ud.err) } diff --git a/update_dataset_test.go b/update_dataset_test.go index 6645a00b..0e5d05ff 100644 --- a/update_dataset_test.go +++ b/update_dataset_test.go @@ -69,6 +69,13 @@ func (uds *updateDatasetSuite) TestPrepared() { uds.False(ds.IsPrepared()) // should apply the prepared to any datasets created from the root uds.True(preparedDs.Where(goqu.Ex{"a": 1}).IsPrepared()) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + // should be prepared by default + ds = goqu.Update("test") + uds.True(ds.IsPrepared()) } func (uds *updateDatasetSuite) TestGetClauses() { @@ -449,6 +456,14 @@ func (uds *updateDatasetSuite) TestExecutor() { uds.NoError(err) uds.Equal([]interface{}{"111 Test Addr", "Test1"}, args) uds.Equal(`UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL) + + defer goqu.SetDefaultPrepared(false) + goqu.SetDefaultPrepared(true) + + updateSQL, args, err = ds.Executor().ToSQL() + uds.NoError(err) + uds.Equal([]interface{}{"111 Test Addr", "Test1"}, args) + uds.Equal(`UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL) } func (uds *updateDatasetSuite) TestSetError() {