diff --git a/delete_dataset.go b/delete_dataset.go
index 0c28a778..5f5474b2 100644
--- a/delete_dataset.go
+++ b/delete_dataset.go
@@ -14,6 +14,7 @@ type DeleteDataset struct {
clauses exp.DeleteClauses
isPrepared bool
queryFactory exec.QueryFactory
+ err error
}
// used internally by database to create a database with a specific adapter
@@ -74,6 +75,7 @@ func (dd *DeleteDataset) copy(clauses exp.DeleteClauses) *DeleteDataset {
clauses: clauses,
isPrepared: dd.isPrepared,
queryFactory: dd.queryFactory,
+ err: dd.err,
}
}
@@ -170,6 +172,22 @@ func (dd *DeleteDataset) Returning(returning ...interface{}) *DeleteDataset {
return dd.copy(dd.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
}
+// Get any error that has been set or nil if no error has been set.
+func (dd *DeleteDataset) Error() error {
+ return dd.err
+}
+
+// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
+// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
+// track those separately.
+func (dd *DeleteDataset) SetError(err error) *DeleteDataset {
+ if dd.err == nil {
+ dd.err = err
+ }
+
+ return dd
+}
+
// Generates a DELETE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
@@ -189,6 +207,9 @@ func (dd *DeleteDataset) Executor() exec.QueryExecutor {
func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(dd.isPrepared)
+ if dd.err != nil {
+ return buf.SetError(dd.err)
+ }
dd.dialect.ToDeleteSQL(buf, dd.clauses)
return buf
}
diff --git a/delete_dataset_test.go b/delete_dataset_test.go
index aa2e4ec3..e171effd 100644
--- a/delete_dataset_test.go
+++ b/delete_dataset_test.go
@@ -425,6 +425,51 @@ func (dds *deleteDatasetSuite) TestExecutor() {
dds.Equal(`DELETE FROM "items" WHERE ("id" > ?)`, dsql)
}
+func (dds *deleteDatasetSuite) TestSetError() {
+
+ err1 := errors.New("error #1")
+ err2 := errors.New("error #2")
+ err3 := errors.New("error #3")
+
+ // Verify initial error set/get works properly
+ md := new(mocks.SQLDialect)
+ ds := Delete("test").SetDialect(md)
+ ds = ds.SetError(err1)
+ dds.Equal(err1, ds.Error())
+ sql, args, err := ds.ToSQL()
+ dds.Empty(sql)
+ dds.Empty(args)
+ dds.Equal(err1, err)
+
+ // Repeated SetError calls on Dataset should not overwrite the original error
+ ds = ds.SetError(err2)
+ dds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ dds.Empty(sql)
+ dds.Empty(args)
+ dds.Equal(err1, err)
+
+ // Builder functions should not lose the error
+ ds = ds.ClearLimit()
+ dds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ dds.Empty(sql)
+ dds.Empty(args)
+ dds.Equal(err1, err)
+
+ // Deeper errors inside SQL generation should still return original error
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToDeleteSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(err3)
+ }).Once()
+
+ sql, args, err = ds.ToSQL()
+ dds.Empty(sql)
+ dds.Empty(args)
+ dds.Equal(err1, err)
+}
+
func TestDeleteDataset(t *testing.T) {
suite.Run(t, new(deleteDatasetSuite))
}
diff --git a/docs/deleting.md b/docs/deleting.md
index c342fd25..b8fba106 100644
--- a/docs/deleting.md
+++ b/docs/deleting.md
@@ -8,9 +8,10 @@
* [Order](#order)
* [Limit](#limit)
* [Returning](#returning)
+ * [SetError](#seterror)
* [Executing](#exec)
-
-
+
+
To create a [`DeleteDataset`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset) you can use
**[`goqu.Delete`](https://godoc.org/github.com/doug-martin/goqu/#Delete)**
@@ -214,6 +215,51 @@ Output:
DELETE FROM "test" RETURNING "test".*
```
+
+**[`SetError`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset.SetError)**
+
+Sometimes while building up a query with goqu you will encounter situations where certain
+preconditions are not met or some end-user contraint has been violated. While you could
+track this error case separately, goqu provides a convenient built-in mechanism to set an
+error on a dataset if one has not already been set to simplify query building.
+
+Set an Error on a dataset:
+
+```go
+func GetDelete(name string, value string) *goqu.DeleteDataset {
+
+ var ds = goqu.Delete("test")
+
+ if len(name) == 0 {
+ return ds.SetError(fmt.Errorf("name is empty"))
+ }
+
+ if len(value) == 0 {
+ return ds.SetError(fmt.Errorf("value is empty"))
+ }
+
+ return ds.Where(goqu.C(name).Eq(value))
+}
+
+```
+
+This error is returned on any subsequent call to `Error` or `ToSQL`:
+
+```go
+var field, value string
+ds = GetDelete(field, value)
+fmt.Println(ds.Error())
+
+sql, args, err = ds.ToSQL()
+fmt.Println(err)
+```
+
+Output:
+```
+name is empty
+name is empty
+```
+
## Executing Deletes
To execute DELETES use [`Database.Delete`](https://godoc.org/github.com/doug-martin/goqu/#Database.Delete) to create your dataset
@@ -265,4 +311,4 @@ Output:
```
Deleted users [ids:=[1 2 3]]
-```
\ No newline at end of file
+```
diff --git a/docs/inserting.md b/docs/inserting.md
index a10b4854..985d8e1a 100644
--- a/docs/inserting.md
+++ b/docs/inserting.md
@@ -8,9 +8,10 @@
* [Insert Map](#insert-map)
* [Insert From Query](#insert-from-query)
* [Returning](#returning)
+ * [SetError](#seterror)
* [Executing](#executing)
-
-
+
+
To create a [`InsertDataset`](https://godoc.org/github.com/doug-martin/goqu/#InsertDataset) you can use
**[`goqu.Insert`](https://godoc.org/github.com/doug-martin/goqu/#Insert)**
@@ -109,7 +110,7 @@ insertSQL, args, _ := ds.ToSQL()
fmt.Println(insertSQL, args)
```
-Output:
+Output:
```sql
INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
```
@@ -373,6 +374,51 @@ Output:
INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "test".*
```
+
+**[`SetError`](https://godoc.org/github.com/doug-martin/goqu/#InsertDataset.SetError)**
+
+Sometimes while building up a query with goqu you will encounter situations where certain
+preconditions are not met or some end-user contraint has been violated. While you could
+track this error case separately, goqu provides a convenient built-in mechanism to set an
+error on a dataset if one has not already been set to simplify query building.
+
+Set an Error on a dataset:
+
+```go
+func GetInsert(name string, value string) *goqu.InsertDataset {
+
+ var ds = goqu.Insert("test")
+
+ if len(field) == 0 {
+ return ds.SetError(fmt.Errorf("name is empty"))
+ }
+
+ if len(value) == 0 {
+ return ds.SetError(fmt.Errorf("value is empty"))
+ }
+
+ return ds.Rows(goqu.Record{name: value})
+}
+
+```
+
+This error is returned on any subsequent call to `Error` or `ToSQL`:
+
+```go
+var field, value string
+ds = GetInsert(field, value)
+fmt.Println(ds.Error())
+
+sql, args, err = ds.ToSQL()
+fmt.Println(err)
+```
+
+Output:
+```
+name is empty
+name is empty
+```
+
## Executing Inserts
@@ -447,4 +493,4 @@ Output:
```
Inserted 1 user id:=5
-```
\ No newline at end of file
+```
diff --git a/docs/selecting.md b/docs/selecting.md
index 116104b5..0b7d9730 100644
--- a/docs/selecting.md
+++ b/docs/selecting.md
@@ -12,6 +12,7 @@
* [`GroupBy`](#group_by)
* [`Having`](#having)
* [`Window`](#window)
+ * [`SetError`](#seterror)
* Executing Queries
* [`ScanStructs`](#scan-structs) - Scans rows into a slice of structs
* [`ScanStruct`](#scan-struct) - Scans a row into a slice a struct, returns false if a row wasnt found
@@ -20,7 +21,7 @@
* [`Count`](#count) - Returns the count for the current query
* [`Pluck`](#pluck) - Selects a single column and stores the results into a slice of primitive values
-
+
To create a [`SelectDataset`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset) you can use
**[`goqu.From`](https://godoc.org/github.com/doug-martin/goqu/#From) and [`goqu.Select`](https://godoc.org/github.com/doug-martin/goqu/#Select)**
@@ -95,7 +96,7 @@ sql, _, _ := goqu.From("test").Select("a", "b", "c").ToSQL()
fmt.Println(sql)
```
-Output:
+Output:
```sql
SELECT "a", "b", "c" FROM "test"
```
@@ -647,12 +648,53 @@ Output:
SELECT ROW_NUMBER() OVER "w" FROM "test" WINDOW "w" AS (PARTITION BY "a" ORDER BY "b")
```
+
+**[`SetError`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.SetError)**
+
+Sometimes while building up a query with goqu you will encounter situations where certain
+preconditions are not met or some end-user contraint has been violated. While you could
+track this error case separately, goqu provides a convenient built-in mechanism to set an
+error on a dataset if one has not already been set to simplify query building.
+
+Set an Error on a dataset:
+
+```go
+func GetSelect(name string) *goqu.SelectDataset {
+
+ var ds = goqu.From("test")
+
+ if len(name) == 0 {
+ return ds.SetError(fmt.Errorf("name is empty"))
+ }
+
+ return ds.Select(name)
+}
+
+```
+
+This error is returned on any subsequent call to `Error` or `ToSQL`:
+
+```go
+var name string = ""
+ds = GetSelect(name)
+fmt.Println(ds.Error())
+
+sql, args, err = ds.ToSQL()
+fmt.Println(err)
+```
+
+Output:
+```
+name is empty
+name is empty
+```
+
## Executing Queries
To execute your query use [`goqu.Database#From`](https://godoc.org/github.com/doug-martin/goqu/#Database.From) to create your dataset
-**[`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanStructs)**
+**[`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanStructs)**
Scans rows into a slice of structs
@@ -742,7 +784,7 @@ fmt.Printf("\n%+v", ids)
[`ScanVal`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanVal)
-Scans a row of 1 column into a primitive value, returns false if a row wasnt found.
+Scans a row of 1 column into a primitive value, returns false if a row wasnt found.
**Note** when using the dataset a `LIMIT` of 1 is automatically applied.
```go
@@ -774,7 +816,7 @@ fmt.Printf("\nCount:= %d", count)
```
-**[`Pluck`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.Pluck)**
+**[`Pluck`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.Pluck)**
Selects a single column and stores the results into a slice of primitive values
diff --git a/docs/updating.md b/docs/updating.md
index 398d5287..a1246702 100644
--- a/docs/updating.md
+++ b/docs/updating.md
@@ -10,9 +10,10 @@
* [Order](#order)
* [Limit](#limit)
* [Returning](#returning)
+ * [SetError](#seterror)
* [Executing](#executing)
-
-
+
+
To create a [`UpdateDataset`](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset) you can use
**[`goqu.Update`](https://godoc.org/github.com/doug-martin/goqu/#Update)**
@@ -268,7 +269,7 @@ UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
**NOTE** The `sqlite3` adapter does not support a multi table syntax.
-`Postgres` Example
+`Postgres` Example
```go
dialect := goqu.Dialect("postgres")
@@ -415,6 +416,51 @@ Output:
UPDATE "test" SET "foo"='bar' RETURNING "test".*
```
+
+**[`SetError`](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.SetError)**
+
+Sometimes while building up a query with goqu you will encounter situations where certain
+preconditions are not met or some end-user contraint has been violated. While you could
+track this error case separately, goqu provides a convenient built-in mechanism to set an
+error on a dataset if one has not already been set to simplify query building.
+
+Set an Error on a dataset:
+
+```go
+func GetUpdate(name string, value string) *goqu.UpdateDataset {
+
+ var ds = goqu.Update("test")
+
+ if len(name) == 0 {
+ return ds.SetError(fmt.Errorf("name is empty"))
+ }
+
+ if len(value) == 0 {
+ return ds.SetError(fmt.Errorf("value is empty"))
+ }
+
+ return ds.Set(goqu.Record{name: value})
+}
+
+```
+
+This error is returned on any subsequent call to `Error` or `ToSQL`:
+
+```go
+var field, value string
+ds = GetUpdate(field, value)
+fmt.Println(ds.Error())
+
+sql, args, err = ds.ToSQL()
+fmt.Println(err)
+```
+
+Output:
+```
+name is empty
+name is empty
+```
+
## Executing Updates
@@ -468,4 +514,4 @@ if err := update.ScanVals(&ids); err != nil {
Output:
```
Updated users with ids [1 2 3]
-```
\ No newline at end of file
+```
diff --git a/insert_dataset.go b/insert_dataset.go
index 1147cb7d..637939be 100644
--- a/insert_dataset.go
+++ b/insert_dataset.go
@@ -12,6 +12,7 @@ type InsertDataset struct {
clauses exp.InsertClauses
isPrepared bool
queryFactory exec.QueryFactory
+ err error
}
var errUnsupportedIntoType = errors.New("unsupported table type, a string or identifier expression is required")
@@ -84,6 +85,7 @@ func (id *InsertDataset) copy(clauses exp.InsertClauses) *InsertDataset {
clauses: clauses,
isPrepared: id.isPrepared,
queryFactory: id.queryFactory,
+ err: id.err,
}
}
@@ -179,6 +181,22 @@ func (id *InsertDataset) ClearOnConflict() *InsertDataset {
return id.OnConflict(nil)
}
+// Get any error that has been set or nil if no error has been set.
+func (id *InsertDataset) Error() error {
+ return id.err
+}
+
+// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
+// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
+// track those separately.
+func (id *InsertDataset) SetError(err error) *InsertDataset {
+ if id.err == nil {
+ id.err = err
+ }
+
+ return id
+}
+
// Generates the default INSERT statement. If Prepared has been called with true then the statement will not be
// interpolated. See examples. When using structs you may specify a column to be skipped in the insert, (e.g. id) by
// specifying a goqu tag with `skipinsert`
@@ -209,6 +227,9 @@ func (id *InsertDataset) Executor() exec.QueryExecutor {
func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(id.isPrepared)
+ if id.err != nil {
+ return buf.SetError(id.err)
+ }
id.dialect.ToInsertSQL(buf, id.clauses)
return buf
}
diff --git a/insert_dataset_test.go b/insert_dataset_test.go
index 7840863a..bb779ed0 100644
--- a/insert_dataset_test.go
+++ b/insert_dataset_test.go
@@ -438,6 +438,51 @@ func (ids *insertDatasetSuite) TestToSQL_ReturnedError() {
md.AssertExpectations(ids.T())
}
+func (ids *insertDatasetSuite) TestSetError() {
+
+ err1 := errors.New("error #1")
+ err2 := errors.New("error #2")
+ err3 := errors.New("error #3")
+
+ // Verify initial error set/get works properly
+ md := new(mocks.SQLDialect)
+ ds := Insert("test").SetDialect(md)
+ ds = ds.SetError(err1)
+ ids.Equal(err1, ds.Error())
+ sql, args, err := ds.ToSQL()
+ ids.Empty(sql)
+ ids.Empty(args)
+ ids.Equal(err1, err)
+
+ // Repeated SetError calls on Dataset should not overwrite the original error
+ ds = ds.SetError(err2)
+ ids.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ ids.Empty(sql)
+ ids.Empty(args)
+ ids.Equal(err1, err)
+
+ // Builder functions should not lose the error
+ ds = ds.Cols("a", "b")
+ ids.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ ids.Empty(sql)
+ ids.Empty(args)
+ ids.Equal(err1, err)
+
+ // Deeper errors inside SQL generation should still return original error
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToInsertSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(err3)
+ }).Once()
+
+ sql, args, err = ds.ToSQL()
+ ids.Empty(sql)
+ ids.Empty(args)
+ ids.Equal(err1, err)
+}
+
func TestInsertDataset(t *testing.T) {
suite.Run(t, new(insertDatasetSuite))
}
diff --git a/select_dataset.go b/select_dataset.go
index 90167bc6..00f218e4 100644
--- a/select_dataset.go
+++ b/select_dataset.go
@@ -16,6 +16,7 @@ type SelectDataset struct {
clauses exp.SelectClauses
isPrepared bool
queryFactory exec.QueryFactory
+ err error
}
var (
@@ -94,6 +95,7 @@ func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
clauses: clauses,
isPrepared: sd.isPrepared,
queryFactory: sd.queryFactory,
+ err: sd.err,
}
}
@@ -511,6 +513,22 @@ func (sd *SelectDataset) ClearWindow() *SelectDataset {
return sd.copy(sd.clauses.ClearWindows())
}
+// Get any error that has been set or nil if no error has been set.
+func (sd *SelectDataset) Error() error {
+ return sd.err
+}
+
+// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
+// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
+// track those separately.
+func (sd *SelectDataset) SetError(err error) *SelectDataset {
+ if sd.err == nil {
+ sd.err = err
+ }
+
+ return sd
+}
+
// Generates a SELECT sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
@@ -658,6 +676,9 @@ func (sd *SelectDataset) PluckContext(ctx context.Context, i interface{}, col st
func (sd *SelectDataset) selectSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(sd.isPrepared)
+ if sd.err != nil {
+ return buf.SetError(sd.err)
+ }
sd.dialect.ToSelectSQL(buf, sd.GetClauses())
return buf
}
diff --git a/select_dataset_test.go b/select_dataset_test.go
index a33cc414..1518c4fc 100644
--- a/select_dataset_test.go
+++ b/select_dataset_test.go
@@ -1465,6 +1465,51 @@ func (sds *selectDatasetSuite) TestPluck_WithPreparedStatement() {
sds.Equal([]string{"Bob", "Sally", "Billy"}, names)
}
+func (sds *selectDatasetSuite) TestSetError() {
+
+ err1 := errors.New("error #1")
+ err2 := errors.New("error #2")
+ err3 := errors.New("error #3")
+
+ // Verify initial error set/get works properly
+ md := new(mocks.SQLDialect)
+ ds := From("test").SetDialect(md)
+ ds = ds.SetError(err1)
+ sds.Equal(err1, ds.Error())
+ sql, args, err := ds.ToSQL()
+ sds.Empty(sql)
+ sds.Empty(args)
+ sds.Equal(err1, err)
+
+ // Repeated SetError calls on Dataset should not overwrite the original error
+ ds = ds.SetError(err2)
+ sds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ sds.Empty(sql)
+ sds.Empty(args)
+ sds.Equal(err1, err)
+
+ // Builder functions should not lose the error
+ ds = ds.ClearWindow()
+ sds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ sds.Empty(sql)
+ sds.Empty(args)
+ sds.Equal(err1, err)
+
+ // Deeper errors inside SQL generation should still return original error
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToInsertSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(err3)
+ }).Once()
+
+ sql, args, err = ds.ToSQL()
+ sds.Empty(sql)
+ sds.Empty(args)
+ sds.Equal(err1, err)
+}
+
func TestSelectDataset(t *testing.T) {
suite.Run(t, new(selectDatasetSuite))
}
diff --git a/truncate_dataset.go b/truncate_dataset.go
index ba63403c..a7985ca9 100644
--- a/truncate_dataset.go
+++ b/truncate_dataset.go
@@ -11,6 +11,7 @@ type TruncateDataset struct {
clauses exp.TruncateClauses
isPrepared bool
queryFactory exec.QueryFactory
+ err error
}
// used internally by database to create a database with a specific adapter
@@ -79,6 +80,7 @@ func (td *TruncateDataset) copy(clauses exp.TruncateClauses) *TruncateDataset {
clauses: clauses,
isPrepared: td.isPrepared,
queryFactory: td.queryFactory,
+ err: td.err,
}
}
@@ -126,6 +128,22 @@ func (td *TruncateDataset) Identity(identity string) *TruncateDataset {
return td.copy(td.clauses.SetOptions(opts))
}
+// Get any error that has been set or nil if no error has been set.
+func (td *TruncateDataset) Error() error {
+ return td.err
+}
+
+// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
+// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
+// track those separately.
+func (td *TruncateDataset) SetError(err error) *TruncateDataset {
+ if td.err == nil {
+ td.err = err
+ }
+
+ return td
+}
+
// Generates a TRUNCATE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
@@ -143,6 +161,9 @@ func (td *TruncateDataset) Executor() exec.QueryExecutor {
func (td *TruncateDataset) truncateSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(td.isPrepared)
+ if td.err != nil {
+ return buf.SetError(td.err)
+ }
td.dialect.ToTruncateSQL(buf, td.clauses)
return buf
}
diff --git a/truncate_dataset_test.go b/truncate_dataset_test.go
index 065d5986..708f763a 100644
--- a/truncate_dataset_test.go
+++ b/truncate_dataset_test.go
@@ -277,6 +277,51 @@ func (tds *truncateDatasetSuite) TestExecutor() {
tds.Equal(`TRUNCATE "table1", "table2"`, tsql)
}
+func (tds *truncateDatasetSuite) TestSetError() {
+
+ err1 := errors.New("error #1")
+ err2 := errors.New("error #2")
+ err3 := errors.New("error #3")
+
+ // Verify initial error set/get works properly
+ md := new(mocks.SQLDialect)
+ ds := Truncate("test").SetDialect(md)
+ ds = ds.SetError(err1)
+ tds.Equal(err1, ds.Error())
+ sql, args, err := ds.ToSQL()
+ tds.Empty(sql)
+ tds.Empty(args)
+ tds.Equal(err1, err)
+
+ // Repeated SetError calls on Dataset should not overwrite the original error
+ ds = ds.SetError(err2)
+ tds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ tds.Empty(sql)
+ tds.Empty(args)
+ tds.Equal(err1, err)
+
+ // Builder functions should not lose the error
+ ds = ds.Cascade()
+ tds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ tds.Empty(sql)
+ tds.Empty(args)
+ tds.Equal(err1, err)
+
+ // Deeper errors inside SQL generation should still return original error
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToTruncateSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(err3)
+ }).Once()
+
+ sql, args, err = ds.ToSQL()
+ tds.Empty(sql)
+ tds.Empty(args)
+ tds.Equal(err1, err)
+}
+
func TestTruncateDataset(t *testing.T) {
suite.Run(t, new(truncateDatasetSuite))
}
diff --git a/update_dataset.go b/update_dataset.go
index b3c137b2..0cd714ac 100644
--- a/update_dataset.go
+++ b/update_dataset.go
@@ -12,6 +12,7 @@ type UpdateDataset struct {
clauses exp.UpdateClauses
isPrepared bool
queryFactory exec.QueryFactory
+ err error
}
var errUnsupportedUpdateTableType = errors.New("unsupported table type, a string or identifier expression is required")
@@ -82,6 +83,7 @@ func (ud *UpdateDataset) copy(clauses exp.UpdateClauses) *UpdateDataset {
clauses: clauses,
isPrepared: ud.isPrepared,
queryFactory: ud.queryFactory,
+ err: ud.err,
}
}
@@ -184,6 +186,22 @@ func (ud *UpdateDataset) Returning(returning ...interface{}) *UpdateDataset {
return ud.copy(ud.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
}
+// Get any error that has been set or nil if no error has been set.
+func (ud *UpdateDataset) Error() error {
+ return ud.err
+}
+
+// Set an error on the dataset if one has not already been set. This error will be returned by a future call to Error
+// or as part of ToSQL. This can be used by end users to record errors while building up queries without having to
+// track those separately.
+func (ud *UpdateDataset) SetError(err error) *UpdateDataset {
+ if ud.err == nil {
+ ud.err = err
+ }
+
+ return ud
+}
+
// Generates an UPDATE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
// See examples.
//
@@ -201,6 +219,9 @@ func (ud *UpdateDataset) Executor() exec.QueryExecutor {
func (ud *UpdateDataset) updateSQLBuilder() sb.SQLBuilder {
buf := sb.NewSQLBuilder(ud.isPrepared)
+ if ud.err != nil {
+ return buf.SetError(ud.err)
+ }
ud.dialect.ToUpdateSQL(buf, ud.clauses)
return buf
}
diff --git a/update_dataset_test.go b/update_dataset_test.go
index f779fa10..61893314 100644
--- a/update_dataset_test.go
+++ b/update_dataset_test.go
@@ -436,6 +436,51 @@ func (uds *updateDatasetSuite) TestExecutor() {
uds.Equal(`UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL)
}
+func (uds *updateDatasetSuite) TestSetError() {
+
+ err1 := errors.New("error #1")
+ err2 := errors.New("error #2")
+ err3 := errors.New("error #3")
+
+ // Verify initial error set/get works properly
+ md := new(mocks.SQLDialect)
+ ds := Update("test").SetDialect(md)
+ ds = ds.SetError(err1)
+ uds.Equal(err1, ds.Error())
+ sql, args, err := ds.ToSQL()
+ uds.Empty(sql)
+ uds.Empty(args)
+ uds.Equal(err1, err)
+
+ // Repeated SetError calls on Dataset should not overwrite the original error
+ ds = ds.SetError(err2)
+ uds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ uds.Empty(sql)
+ uds.Empty(args)
+ uds.Equal(err1, err)
+
+ // Builder functions should not lose the error
+ ds = ds.ClearLimit()
+ uds.Equal(err1, ds.Error())
+ sql, args, err = ds.ToSQL()
+ uds.Empty(sql)
+ uds.Empty(args)
+ uds.Equal(err1, err)
+
+ // Deeper errors inside SQL generation should still return original error
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToUpdateSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(err3)
+ }).Once()
+
+ sql, args, err = ds.ToSQL()
+ uds.Empty(sql)
+ uds.Empty(args)
+ uds.Equal(err1, err)
+}
+
func TestUpdateDataset(t *testing.T) {
suite.Run(t, new(updateDatasetSuite))
}