diff --git a/HISTORY.md b/HISTORY.md
index f7fc8185..085f1d2e 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,3 +1,33 @@
+## v8.0.0
+
+A major change the the API was made in `v8` to seperate concerns between the different SQL statement types.
+
+**Why the change?**
+
+1. There were feature requests that could not be cleanly implemented with everything in a single dataset.
+2. Too much functionality was encapsulated in a single datastructure.
+ * It was unclear what methods could be used for each SQL statement type.
+ * Changing a feature for one statement type had the possiblity of breaking another statement type.
+ * Test coverage was decent but was almost solely concerned about SELECT statements, breaking them up allowed for focused testing on each statement type.
+ * Most the SQL generation methods (`ToInsertSQL`, `ToUpdateSQL` etc.) took arguments which lead to an ugly API that was not uniform for each statement type, and proved to be inflexible.
+
+**What Changed**
+
+There are now five dataset types, `SelectDataset`, `InsertDataset`, `UpdateDataset`, `DeleteDataset` and `TruncateDataset`
+
+Each dataset type has its own entry point.
+
+* `goqu.From`, `Database#From`, `DialectWrapper#From` - Create SELECT
+* `goqu.Insert`, `Database#Insert`, `DialectWrapper#Insert` - Create INSERT
+* `goqu.Update`, `Database#db.Update`, `DialectWrapper#Update` - Create UPDATE
+* `goqu.Delete`, `Database#Delete`, `DialectWrapper#Delete` - Create DELETE
+* `goqu.Truncate`, `Database#Truncate`, `DialectWrapper#Truncate` - Create TRUNCATE
+
+`ToInsertSQL`, `ToUpdateSQL`, `ToDeleteSQL`, and `ToTruncateSQL` (and variations of them) methods have been removed from the `SelectDataset`. Instead use the `ToSQL` methods on each dataset type.
+
+Each dataset type will have an `Executor` and `ToSQL` method so a common interface can be created for each type.
+
+
## v7.4.0
* [FIXED] literalTime use t.UTC() , This behavior is different from the original sql.DB [#106](https://github.com/doug-martin/goqu/issues/106) - [chen56](https://github.com/chen56)
diff --git a/README.md b/README.md
index db51bea4..2b0d8c6d 100644
--- a/README.md
+++ b/README.md
@@ -9,28 +9,30 @@
[![Build Status](https://travis-ci.org/doug-martin/goqu.svg?branch=master)](https://travis-ci.org/doug-martin/goqu)
[![GoDoc](https://godoc.org/github.com/doug-martin/goqu?status.png)](http://godoc.org/github.com/doug-martin/goqu)
[![codecov](https://codecov.io/gh/doug-martin/goqu/branch/master/graph/badge.svg)](https://codecov.io/gh/doug-martin/goqu)
-[![Go Report Card](https://goreportcard.com/badge/github.com/doug-martin/goqu/v7)](https://goreportcard.com/report/github.com/doug-martin/goqu/v7)
+[![Go Report Card](https://goreportcard.com/badge/github.com/doug-martin/goqu/v8)](https://goreportcard.com/report/github.com/doug-martin/goqu/v8)
`goqu` is an expressive SQL builder and executor
+If you are upgrading from an older version please read the [Migrating Between Versions](./docs/version_migration.md) docs.
+
+
## Installation
If using go modules.
```sh
-go get -u github.com/doug-martin/goqu/v7
+go get -u github.com/doug-martin/goqu/v8
```
If you are not using go modules...
-**NOTE** You should still be able to use this package if you are using go version `>v1.10` but, you will need to drop the version from the package. `import "github.com/doug-martin/goqu/v7` -> `import "github.com/doug-martin/goqu"`
+**NOTE** You should still be able to use this package if you are using go version `>v1.10` but, you will need to drop the version from the package. `import "github.com/doug-martin/goqu/v8` -> `import "github.com/doug-martin/goqu"`
```sh
go get -u github.com/doug-martin/goqu
```
-
-[Migrating Between Versions](#migrating)
+### [Migrating Between Versions](./docs/version_migration.md)
## Features
@@ -60,988 +62,197 @@ We tried a few other sql builders but each was a thin wrapper around sql fragmen
* Easily scan results into primitive values and structs
* Use the native sql.Db methods when desired
-## Usage
-
-* [Dialect](#dialect)
- * [Postgres](#postgres)
- * [MySQL](#mysql)
- * [SQLite3](#sqlite3)
-* [Dataset](#dataset)
- * [Building SQL](#building-sql)
- * [Expressions](#expressions)
- * [`Ex{}`](#ex) - Expression map filtering
- * [`ExOr{}`](#ex-or) - ORed expression map filtering
- * [`S()`](#S) - Schema identifiers
- * [`T()`](#T) - Table identifiers
- * [`C()`](#C) - Column identifiers
- * [`I()`](#I) - Parsing identifiers wit
- * [`L()`](#L) - Literal SQL expressions
- * [`And()`](#and) - ANDed sql expressions
- * [`OR()`](#or) - ORed sql expressions
- * [Complex Example](#complex-example)
- * [Querying](#querying)
- * [Executing Queries](#executing-queries)
- * [Dataset](#dataset)
- * [Prepared Statements](#dataset_prepared)
-* [Database](#database)
- * [Transactions](#transactions)
-* [Logging](#logging)
-* [Custom Dialects](#custom-dialects)
-
-
-## Dialect
-
-Dialects allow goqu the build the correct SQL for each database. There are three dialects that come packaged with `goqu`
-
-* [mysql](./dialect/mysql/mysql.go) - `import _ "github.com/doug-martin/goqu/v7/dialect/mysql"`
-* [postgres](./dialect/postgres/postgres.go) - `import _ "github.com/doug-martin/goqu/v7/dialect/postgres"`
-* [sqlite3](./dialect/sqlite3/sqlite3.go) - `import _ "github.com/doug-martin/goqu/v7/dialect/sqlite3"`
-
-**NOTE** Dialects work like drivers in go where they are not registered until you import the package.
-
-Below are examples for each dialect. Notice how the dialect is imported and then looked up using `goqu.Dialect`
-
-
-### Postgres
-```go
-import (
- "fmt"
- "github.com/doug-martin/goqu/v7"
- // import the dialect
- _ "github.com/doug-martin/goqu/v7/dialect/postgres"
-)
-
-// look up the dialect
-dialect := goqu.Dialect("postgres")
+## Docs
-// use dialect.From to get a dataset to build your SQL
-ds := dialect.From("test").Where(goqu.Ex{"id": 10})
-sql, args, err := ds.ToSQL()
-if err != nil{
- fmt.Println("An error occurred while generating the SQL", err.Error())
-}else{
- fmt.Println(sql, args)
-}
-```
+* [Dialect](./docs/dialect.md) - Introduction to different dialects (`mysql`, `postgres`, `sqlite3` etc)
+* [Expressions](./docs/expressions.md) - Introduction to `goqu` expressions and common examples.
+* [Select Dataset](./docs/selecting.md) - Docs and examples about creating and executing SELECT sql statements.
+* [Insert Dataset](./docs/inserting.md) - Docs and examples about creating and executing INSERT sql statements.
+* [Update Dataset](./docs/updating.md) - Docs and examples about creating and executing UPDATE sql statements.
+* [Delete Dataset](./docs/deleting.md) - Docs and examples about creating and executing DELETE sql statements.
+* [Prepared Statements](./docs/interpolation.md) - Docs about interpolation and prepared statements in `goqu`.
+* [Database](./docs/database.md) - Docs and examples of using a Database to execute queries in `goqu`
-Output:
-```
-SELECT * FROM "test" WHERE "id" = 10 []
-```
+## Quick Examples
-
-### MySQL
-```go
-import (
- "fmt"
- "github.com/doug-martin/goqu/v7"
- // import the dialect
- _ "github.com/doug-martin/goqu/v7/dialect/mysql"
-)
+### Select
-// look up the dialect
-dialect := goqu.Dialect("mysql")
+See the [select dataset](./docs/selecting.md) docs for more in depth examples
-// use dialect.From to get a dataset to build your SQL
-ds := dialect.From("test").Where(goqu.Ex{"id": 10})
-sql, args, err := ds.ToSQL()
-if err != nil{
- fmt.Println("An error occurred while generating the SQL", err.Error())
-}else{
- fmt.Println(sql, args)
-}
+```go
+sql, _, _ := goqu.From("test").ToSQL()
+fmt.Println(sql)
```
Output:
+
```
-SELECT * FROM `test` WHERE `id` = 10 []
+SELECT * FROM "test"
```
-
-### SQLite3
```go
-import (
- "fmt"
- "github.com/doug-martin/goqu/v7"
- // import the dialect
- _ "github.com/doug-martin/goqu/v7/dialect/sqlite3"
-)
-
-// look up the dialect
-dialect := goqu.Dialect("sqlite3")
-
-// use dialect.From to get a dataset to build your SQL
-ds := dialect.From("test").Where(goqu.Ex{"id": 10})
-sql, args, err := ds.ToSQL()
-if err != nil{
- fmt.Println("An error occurred while generating the SQL", err.Error())
-}else{
- fmt.Println(sql, args)
-}
+sql, _, _ := goqu.From("test").Where(goqu.Ex{
+ "d": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
```
Output:
+
```
-SELECT * FROM `test` WHERE `id` = 10 []
+SELECT * FROM "test" WHERE ("d" IN ('a', 'b', 'c'))
```
-
-## Dataset
+### Insert
-A [`goqu.Dataset`](https://godoc.org/github.com/doug-martin/goqu#Dataset) is the most commonly used data structure used in `goqu`. A `Dataset` can be used to:
-* [build SQL](#building-sql) - When used with a `dialect` and `expressions` a dataset is an expressive SQL builder
-* [execute queries](#querying) - When used with a `goqu.Database` a `goqu.Dataset` can be used to:
- * [`ScanStruct`](#ds-scan-struct) - scan into a struct
- * [`ScanStructs`](#ds-scan-structs) - scan into a slice of structs
- * [`ScanVal`](#ds-scan-val) - scan into a primitive value or a `driver.Valuer`
- * [`ScanVals`](#ds-scan-vals) - scan into a slice of primitive values or `driver.Valuer`s
- * [`Count`](#ds-count) - count the number of records in a table
- * [`Pluck`](#ds-pluck) - pluck a column from a table
- * [`Insert`](#ds-insert) - insert records into a table
- * [`Update`](#ds-update) - update records in a table
- * [`Delete`](#ds-delete) - delete records in a table
+See the [insert dataset](./docs/inserting.md) docs for more in depth examples
-
-### Building SQL
-
-To build SQL with a dialect you can use `goqu.Dialect`
+```go
+ds := goqu.Insert("user").
+ Cols("first_name", "last_name").
+ Vals(
+ goqu.Vals{"Greg", "Farley"},
+ goqu.Vals{"Jimmy", "Stewart"},
+ goqu.Vals{"Jeff", "Jeffers"},
+ )
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
-**NOTE** if you use do not create a `goqu.Database` you can only create SQL
+Output:
+```sql
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+```
```go
-import (
- "fmt"
- "github.com/doug-martin/goqu/v7"
- _ "github.com/doug-martin/goqu/v7/dialect/postgres"
+ds := goqu.Insert("user").Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+ goqu.Record{"first_name": "Jimmy", "last_name": "Stewart"},
+ goqu.Record{"first_name": "Jeff", "last_name": "Jeffers"},
)
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
-dialect := goqu.Dialect("postgres")
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+```
-//interpolated sql
-ds := dialect.From("test").Where(goqu.Ex{"id": 10})
-sql, args, err := ds.ToSQL()
-if err != nil{
- fmt.Println("An error occurred while generating the SQL", err.Error())
-}else{
- fmt.Println(sql, args)
-}
-//prepared sql
-sql, args, err := ds.Prepared(true).ToSQL()
-if err != nil{
- fmt.Println("An error occurred while generating the SQL", err.Error())
-}else{
- fmt.Println(sql, args)
+```go
+type User struct {
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
}
-
+ds := goqu.Insert("user").Rows(
+ User{FirstName: "Greg", LastName: "Farley"},
+ User{FirstName: "Jimmy", LastName: "Stewart"},
+ User{FirstName: "Jeff", LastName: "Jeffers"},
+)
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
```
Output:
```
-SELECT * FROM "test" WHERE "id" = 10 []
-SELECT * FROM "test" WHERE "id" = $1 [10]
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
```
-
-### Expressions
-
-`goqu` provides an idiomatic DSL for generating SQL. Datasets only act as a clause builder (i.e. Where, From, Select), most of these clause methods accept Expressions which are the building blocks for your SQL statement, you can think of them as fragments of SQL.
-
-The entry points for expressions are:
-
-
-* [`Ex{}`](https://godoc.org/github.com/doug-martin/goqu#Ex) - A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause. By default `Ex` will use the equality operator except in cases where the equality operator will not work, see the example below.
- ```go
- sql, _, _ := db.From("items").Where(goqu.Ex{
- "col1": "a",
- "col2": 1,
- "col3": true,
- "col4": false,
- "col5": nil,
- "col6": []string{"a", "b", "c"},
- }).ToSQL()
- fmt.Println(sql)
- ```
-
- Output:
- ```sql
- SELECT * FROM "items" WHERE (("col1" = 'a') AND ("col2" = 1) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IS NULL) AND ("col6" IN ('a', 'b', 'c')))
- ```
-
- You can also use the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) map which allows you to create more complex expressions using the map syntax. When using the `Op` map the key is the name of the comparison you want to make (e.g. `"neq"`, `"like"`, `"is"`, `"in"`), the key is case insensitive.
- ```go
- sql, _, _ := db.From("items").Where(goqu.Ex{
- "col1": goqu.Op{"neq": "a"},
- "col3": goqu.Op{"isNot": true},
- "col6": goqu.Op{"notIn": []string{"a", "b", "c"}},
- }).ToSQL()
- fmt.Println(sql)
- ```
-
- Output:
- ```sql
- SELECT * FROM "items" WHERE (("col1" != 'a') AND ("col3" IS NOT TRUE) AND ("col6" NOT IN ('a', 'b', 'c')))
- ```
- For a more complete examples see the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) and [`Ex`](https://godoc.org/github.com/doug-martin/goqu#Ex) docs
-
-
-* [`ExOr{}`](https://godoc.org/github.com/doug-martin/goqu#ExOr) - A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause. By default `ExOr` will use the equality operator except in cases where the equality operator will not work, see the example below.
- ```go
- sql, _, _ := db.From("items").Where(goqu.ExOr{
- "col1": "a",
- "col2": 1,
- "col3": true,
- "col4": false,
- "col5": nil,
- "col6": []string{"a", "b", "c"},
- }).ToSQL()
- fmt.Println(sql)
- ```
-
- Output:
- ```sql
- SELECT * FROM "items" WHERE (("col1" = 'a') OR ("col2" = 1) OR ("col3" IS TRUE) OR ("col4" IS FALSE) OR ("col5" IS NULL) OR ("col6" IN ('a', 'b', 'c')))
- ```
-
- You can also use the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) map which allows you to create more complex expressions using the map syntax. When using the `Op` map the key is the name of the comparison you want to make (e.g. `"neq"`, `"like"`, `"is"`, `"in"`), the key is case insensitive.
-
- ```go
- sql, _, _ := db.From("items").Where(goqu.ExOr{
- "col1": goqu.Op{"neq": "a"},
- "col3": goqu.Op{"isNot": true},
- "col6": goqu.Op{"notIn": []string{"a", "b", "c"}},
- }).ToSQL()
- fmt.Println(sql)
- ```
-
- Output:
- ```sql
- SELECT * FROM "items" WHERE (("col1" != 'a') OR ("col3" IS NOT TRUE) OR ("col6" NOT IN ('a', 'b', 'c')))
- ```
- For a more complete examples see the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) and [`ExOr`](https://godoc.org/github.com/doug-martin/goqu#Ex) docs
-
-
-* [`S()`](https://godoc.org/github.com/doug-martin/goqu#S) - An Identifier that represents a schema. With a schema identifier you can fully qualify tables and columns.
- ```go
- s := goqu.S("my_schema")
-
- // "my_schema"."my_table"
- t := s.Table("my_table")
-
- // "my_schema"."my_table"."my_column"
-
- sql, _, _ := goqu.From(t).Select(t.Col("my_column").ToSQL()
- // SELECT "my_schema"."my_table"."my_column" FROM "my_schema"."my_table"
- fmt.Println(sql)
- ```
-
-
-* [`T()`](https://godoc.org/github.com/doug-martin/goqu#T) - An Identifier that represents a Table. With a Table identifier you can fully qualify columns.
- ```go
- t := s.Table("my_table")
-
- sql, _, _ := goqu.From(t).Select(t.Col("my_column").ToSQL()
- // SELECT "my_table"."my_column" FROM "my_table"
- fmt.Println(sql)
-
- // qualify the table with a schema
- sql, _, _ := goqu.From(t.Schema("my_schema")).Select(t.Col("my_column").ToSQL()
- // SELECT "my_table"."my_column" FROM "my_schema"."my_table"
- fmt.Println(sql)
- ```
-
-
-* [`C()`](https://godoc.org/github.com/doug-martin/goqu#C) - An Identifier that represents a Column. See the [docs]((https://godoc.org/github.com/doug-martin/goqu#C)) for more examples
- ```go
- sql, _, _ := goqu.From("table").Where(goqu.C("col").Eq(10)).ToSQL()
- // SELECT * FROM "table" WHERE "col" = 10
- fmt.Println(sql)
- ```
-
-
-* [`I()`](https://godoc.org/github.com/doug-martin/goqu#I) - An Identifier represents a schema, table, or column or any combination. `I` parses identifiers seperated by a `.` character.
- ```go
- // with three parts it is assumed you have provided a schema, table and column
- goqu.I("my_schema.table.col") == goqu.S("my_schema").Table("table").Col("col")
-
- // with two parts it is assumed you have provided a table and column
- goqu.I("table.col") == goqu.T("table").Col("col")
-
- // with a single value it is the same as calling goqu.C
- goqu.I("col") == goqu.C("col")
-
- ```
-
-
-* [`L()`](https://godoc.org/github.com/doug-martin/goqu#L) - An SQL literal. You may find yourself in a situation where an IdentifierExpression cannot expression an SQL fragment that your database supports. In that case you can use a LiteralExpression
- ```go
- // manual casting
- goqu.L(`"json"::TEXT = "other_json"::text`)
-
- // custom function invocation
- goqu.L(`custom_func("a")`)
-
- // postgres JSON access
- goqu.L(`"json_col"->>'someField'`).As("some_field")
- ```
-
- You can also use placeholders in your literal with a `?` character. `goqu` will handle changing it to what the dialect needs (e.g. `?` mysql, `$1` postgres, `?` sqlite3).
-
- **NOTE** If your query is not prepared the placeholders will be properly interpolated.
-
- ```go
- goqu.L("col IN (?, ?, ?)", "a", "b", "c")
- ```
-
- Putting it together
-
- ```go
- ds := db.From("test").Where(
- goqu.L(`("json"::TEXT = "other_json"::TEXT)`),
- goqu.L("col IN (?, ?, ?)", "a", "b", "c"),
- )
-
- sql, args, _ := ds.ToSQL()
- fmt.Println(sql, args)
-
- sql, args, _ := ds.Prepared(true).ToSQL()
- fmt.Println(sql, args)
- ```
-
- Output:
- ```sql
- SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ('a', 'b', 'c') []
- -- assuming postgres dialect
- SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ($1, $2, $3) [a, b, c]
- ```
-
-
-* [`And()`](https://godoc.org/github.com/doug-martin/goqu#And) - You can use the `And` function to AND multiple expressions together.
-
- **NOTE** By default goqu will AND expressions together
-
- ```go
- ds := goqu.From("test").Where(
- goqu.And(
- goqu.C("col").Gt(10),
- goqu.C("col").Lt(20),
- ),
- )
- sql, args, _ := ds.ToSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = ds.Prepared(true).ToSQL()
- fmt.Println(sql, args)
- ```
-
- Output:
- ```sql
- SELECT * FROM "test" WHERE (("col" > 10) AND ("col" < 20)) []
- SELECT * FROM "test" WHERE (("col" > ?) AND ("col" < ?)) [10 20]
- ```
-
-
-* [`Or()`](https://godoc.org/github.com/doug-martin/goqu#Or) - You can use the `Or` function to OR multiple expressions together.
-
- ```go
- ds := goqu.From("test").Where(
- goqu.Or(
- goqu.C("col").Eq(10),
- goqu.C("col").Eq(20),
- ),
- )
- sql, args, _ := ds.ToSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = ds.Prepared(true).ToSQL()
- fmt.Println(sql, args)
- ```
-
- Output:
- ```sql
- SELECT * FROM "test" WHERE (("col" = 10) OR ("col" = 20)) []
- SELECT * FROM "test" WHERE (("col" = ?) OR ("col" = ?)) [10 20]
- ```
-
- You can also use `Or` and `And` functions in tandem which will give you control not only over how the Expressions are joined together, but also how they are grouped
-
- ```go
- ds := goqu.From("items").Where(
- goqu.Or(
- goqu.C("a").Gt(10),
- goqu.And(
- goqu.C("b").Eq(100),
- goqu.C("c").Neq("test"),
- ),
- ),
- )
- sql, args, _ := ds.ToSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = ds.Prepared(true).ToSQL()
- fmt.Println(sql, args)
- ```
-
- Output:
- ```sql
- SELECT * FROM "items" WHERE (("a" > 10) OR (("b" = 100) AND ("c" != 'test'))) []
- SELECT * FROM "items" WHERE (("a" > ?) OR (("b" = ?) AND ("c" != ?))) [10 100 test]
- ```
-
- You can also use Or with the map syntax
- ```go
- ds := goqu.From("test").Where(
- goqu.Or(
- // Ex will be anded together
- goqu.Ex{
- "col1": 1,
- "col2": true,
- },
- goqu.Ex{
- "col3": nil,
- "col4": "foo",
- },
- ),
- )
- sql, args, _ := ds.ToSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = ds.Prepared(true).ToSQL()
- fmt.Println(sql, args)
- ```
-
- Output:
- ```sql
- SELECT * FROM "test" WHERE ((("col1" = 1) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = 'foo'))) []
- SELECT * FROM "test" WHERE ((("col1" = ?) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = ?))) [1 foo]
- ```
-
-### Complex Example
-
-Using the Ex map syntax
```go
-ds := db.From("test").
- Select(goqu.COUNT("*")).
- InnerJoin(goqu.I("test2"), goqu.On(goqu.Ex{"test.fkey": goqu.I("test2.id")})).
- LeftJoin(goqu.I("test3"), goqu.On(goqu.Ex{"test2.fkey": goqu.I("test3.id")})).
- Where(
- goqu.Ex{
- "test.name": goqu.Op{"like": regexp.MustCompile("^(a|b)")},
- "test2.amount": goqu.Op{"isNot": nil},
- },
- goqu.ExOr{
- "test3.id": nil,
- "test3.status": []string{"passed", "active", "registered"},
- },
- ).
- Order(goqu.I("test.created").Desc().NullsLast()).
- GroupBy(goqu.I("test.user_id")).
- Having(goqu.AVG("test3.age").Gt(10))
-
-sql, args, _ := ds.ToSQL()
-fmt.Println(sql)
-
-sql, args, _ := ds.Prepared(true).ToSQL()
-fmt.Println(sql)
+ds := goqu.Insert("user").Prepared(true).
+ FromQuery(goqu.From("other_table"))
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
```
-Using the Expression syntax
-```go
-ds := db.From("test").
- Select(goqu.COUNT("*")).
- InnerJoin(goqu.I("test2"), goqu.On(goqu.I("test.fkey").Eq(goqu.I("test2.id")))).
- LeftJoin(goqu.I("test3"), goqu.On(goqu.I("test2.fkey").Eq(goqu.I("test3.id")))).
- Where(
- goqu.I("test.name").Like(regexp.MustCompile("^(a|b)")),
- goqu.I("test2.amount").IsNotNull(),
- goqu.Or(
- goqu.I("test3.id").IsNull(),
- goqu.I("test3.status").In("passed", "active", "registered"),
- ),
- ).
- Order(goqu.I("test.created").Desc().NullsLast()).
- GroupBy(goqu.I("test.user_id")).
- Having(goqu.AVG("test3.age").Gt(10))
-
-sql, args, _ := ds.ToSQL()
-fmt.Println(sql)
-
-sql, args, _ := ds.Prepared(true).ToSQL()
-fmt.Println(sql)
+Output:
```
-
-Both examples generate the following SQL
-
-```sql
--- interpolated
-SELECT COUNT(*)
-FROM "test"
- INNER JOIN "test2" ON ("test"."fkey" = "test2"."id")
- LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id")
-WHERE ((("test"."name" ~ '^(a|b)') AND ("test2"."amount" IS NOT NULL)) AND
- (("test3"."id" IS NULL) OR ("test3"."status" IN ('passed', 'active', 'registered'))))
-GROUP BY "test"."user_id"
-HAVING (AVG("test3"."age") > 10)
-ORDER BY "test"."created" DESC NULLS LAST []
-
--- prepared
-SELECT COUNT(*)
-FROM "test"
- INNER JOIN "test2" ON ("test"."fkey" = "test2"."id")
- LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id")
-WHERE ((("test"."name" ~ ?) AND ("test2"."amount" IS NOT NULL)) AND
- (("test3"."id" IS NULL) OR ("test3"."status" IN (?, ?, ?))))
-GROUP BY "test"."user_id"
-HAVING (AVG("test3"."age") > ?)
-ORDER BY "test"."created" DESC NULLS LAST [^(a|b) passed active registered 10]
+INSERT INTO "user" SELECT * FROM "other_table" []
```
-
-## Querying
-
-`goqu` also has basic query support through the use of either the Database or the Dataset.
-
-
-### Executing Queries
-
-You can also create a `goqu.Database` instance to query records.
-
-In the example below notice that we imported the dialect and driver for side effect only.
-
```go
-import (
- "database/sql"
- "github.com/doug-martin/goqu/v7"
- _ "github.com/doug-martin/goqu/v7/dialect/postgres"
- _ "github.com/lib/pq"
-)
-
-dialect := goqu.Dialect("postgres")
-
-pgDb, err := sql.Open("postgres", "user=postgres dbname=goqupostgres sslmode=disable ")
-if err != nil {
- panic(err.Error())
-}
-db := dialect.DB(pgDb)
-
-// "SELECT COUNT(*) FROM "user";
-if count, err := db.From("user").Count(); err != nil {
- fmt.Println(err.Error())
-}else{
- fmt.Printf("User count = %d", count)
-}
+ds := goqu.Insert("user").Prepared(true).
+ Cols("first_name", "last_name").
+ FromQuery(goqu.From("other_table").Select("fn", "ln"))
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
```
-
-* [`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#Dataset.ScanStructs) - scans rows into a slice of structs
-
- **NOTE** [`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#Dataset.ScanStructs) will only select the columns that can be scanned in to the structs unless you have explicitly selected certain columns.
-
- ```go
- type User struct{
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- }
-
- var users []User
- //SELECT "first_name", "last_name" FROM "user";
- if err := db.From("user").ScanStructs(&users); err != nil{
- panic(err.Error())
- }
- fmt.Printf("\n%+v", users)
-
- var users []User
- //SELECT "first_name" FROM "user";
- if err := db.From("user").Select("first_name").ScanStructs(&users); err != nil{
- panic(err.Error())
- }
- fmt.Printf("\n%+v", users)
- ```
-
-
-* [`ScanStruct`](http://godoc.org/github.com/doug-martin/goqu#Dataset.ScanStruct) - scans a row into a slice a struct, returns false if a row wasnt found
-
- **NOTE** [`ScanStruct`](http://godoc.org/github.com/doug-martin/goqu#Dataset.ScanStruct) will only select the columns that can be scanned in to the struct unless you have explicitly selected certain columns.
-
- ```go
- type User struct{
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- }
-
- var user User
- // SELECT "first_name", "last_name" FROM "user" LIMIT 1;
- found, err := db.From("user").ScanStruct(&user)
- if err != nil{
- fmt.Println(err.Error())
- return
- }
- if !found {
- fmt.Println("No user found")
- } else {
- fmt.Printf("\nFound user: %+v", user)
- }
- ```
-
-
- **NOTE** Using the `goqu.SetColumnRenameFunction` function, you can change the function that's used to rename struct fields when struct tags aren't defined
-
- ```go
- import "strings"
-
- goqu.SetColumnRenameFunction(strings.ToUpper)
-
- type User struct{
- FirstName string
- LastName string
- }
-
- var user User
- //SELECT "FIRSTNAME", "LASTNAME" FROM "user" LIMIT 1;
- found, err := db.From("user").ScanStruct(&user)
- // ...
- ```
-
-* [`ScanVals`](http://godoc.org/github.com/doug-martin/goqu#Dataset.ScanVals) - scans a rows of 1 column into a slice of primitive values
- ```go
- var ids []int64
- if err := db.From("user").Select("id").ScanVals(&ids); err != nil{
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("\n%+v", ids)
- ```
-
-
-* [`ScanVal`](http://godoc.org/github.com/doug-martin/goqu#Dataset.ScanVal) - 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
- var id int64
- found, err := db.From("user").Select("id").ScanVal(&id)
- if err != nil{
- fmt.Println(err.Error())
- return
- }
- if !found{
- fmt.Println("No id found")
- }else{
- fmt.Printf("\nFound id: %d", id)
- }
- ```
-
-
-* [`Count`](http://godoc.org/github.com/doug-martin/goqu#Dataset.Count) - Returns the count for the current query
- ```go
- count, err := db.From("user").Count()
- if err != nil{
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("\nCount:= %d", count)
- ```
-
-
-* [`Pluck`](http://godoc.org/github.com/doug-martin/goqu#Dataset.Pluck) - Selects a single column and stores the results into a slice of primitive values
- ```go
- var ids []int64
- if err := db.From("user").Pluck(&ids, "id"); err != nil{
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("\nIds := %+v", ids)
- ```
-
-
-* [`Insert`](http://godoc.org/github.com/doug-martin/goqu#Dataset.Insert) - Creates an `INSERT` statement and returns a [`QueryExecutor`](http://godoc.org/github.com/doug-martin/goqu/exec/#QueryExecutor) to execute the statement
- ```go
- insert := db.From("user").Insert(goqu.Record{
- "first_name": "Bob",
- "last_name": "Yukon",
- "created": time.Now(),
- })
- if _, err := insert.Exec(); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
- Insert will also handle multi inserts if supported by the database
-
- ```go
- users := []goqu.Record{
- {"first_name": "Bob", "last_name": "Yukon", "created": time.Now()},
- {"first_name": "Sally", "last_name": "Yukon", "created": time.Now()},
- {"first_name": "Jimmy", "last_name": "Yukon", "created": time.Now()},
- }
- if _, err := db.From("user").Insert(users).Exec(); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
- If your database supports the `RETURN` clause you can also use the different Scan methods to get results
- ```go
- var ids []int64
- users := []goqu.Record{
- {"first_name": "Bob", "last_name": "Yukon", "created": time.Now()},
- {"first_name": "Sally", "last_name": "Yukon", "created": time.Now()},
- {"first_name": "Jimmy", "last_name": "Yukon", "created": time.Now()},
- }
- if err := db.From("user").Returning(goqu.C("id")).Insert(users).ScanVals(&ids); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
-
-* [`Update`](http://godoc.org/github.com/doug-martin/goqu#Dataset.Update) - Creates an `UPDATE` statement and returns [`QueryExecutor`](http://godoc.org/github.com/doug-martin/goqu/exec/#QueryExecutor) to execute the statement
-
- ```go
- update := db.From("user").
- Where(goqu.C("status").Eq("inactive")).
- Update(goqu.Record{"password": nil, "updated": time.Now()})
- if _, err := update.Exec(); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
- If your database supports the `RETURN` clause you can also use the different Scan methods to get results
- ```go
- var ids []int64
- update := db.From("user").
- Where(goqu.Ex{"status":"inactive"}).
- Returning("id").
- Update(goqu.Record{"password": nil, "updated": time.Now()})
- if err := update.ScanVals(&ids); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
-
-* [`Delete`](http://godoc.org/github.com/doug-martin/goqu#Dataset.Delete) - Creates an `DELETE` statement and returns a [`QueryExecutor`](http://godoc.org/github.com/doug-martin/goqu/exec/#QueryExecutor) to execute the statement
- ```go
- delete := db.From("invoice").
- Where(goqu.Ex{"status":"paid"}).
- Delete()
- if _, err := delete.Exec(); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
- If your database supports the `RETURN` clause you can also use the different Scan methods to get results
-
- ```go
- var ids []int64
- delete := db.From("invoice").
- Where(goqu.C("status").Eq("paid")).
- Returning(goqu.C("id")).
- Delete()
- if err := delete.ScanVals(&ids); err != nil{
- fmt.Println(err.Error())
- return
- }
- ```
-
-
-#### Prepared Statements
-
-By default the `Dataset` will interpolate all parameters, if you do not want to have values interpolated you can use the [`Prepared`](http://godoc.org/github.com/doug-martin/goqu#Dataset.Prepared) method to prevent this.
-
-**Note** For the examples all placeholders are `?` this will be dialect specific when using other examples (e.g. Postgres `$1, $2...`)
-
-```go
-
-preparedDs := db.From("items").Prepared(true)
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") SELECT "fn", "ln" FROM "other_table" []
+```
-sql, args, _ := preparedDs.Where(goqu.Ex{
- "col1": "a",
- "col2": 1,
- "col3": true,
- "col4": false,
- "col5": []string{"a", "b", "c"},
-}).ToSQL()
-fmt.Println(sql, args)
+### Update
-sql, args, _ = preparedDs.ToInsertSQL(
- goqu.Record{"name": "Test1", "address": "111 Test Addr"},
- goqu.Record{"name": "Test2", "address": "112 Test Addr"},
-)
-fmt.Println(sql, args)
+See the [update dataset](./docs/updating.md) docs for more in depth examples
-sql, args, _ = preparedDs.ToUpdateSQL(
+```go
+sql, args, _ := goqu.Update("items").Set(
goqu.Record{"name": "Test", "address": "111 Test Addr"},
-)
-fmt.Println(sql, args)
-
-sql, args, _ = preparedDs.
- Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
- ToDeleteSQL()
+).ToSQL()
fmt.Println(sql, args)
-
-// Output:
-// SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IN (?, ?, ?))) [a 1 a b c]
-// INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
-// UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
-// DELETE FROM "items" WHERE ("id" > ?) [10]
```
-When setting prepared to true executing the SQL using the different querying methods will also use the non-interpolated SQL also.
-
-```go
-var items []Item
-sql, args, _ := db.From("items").Prepared(true).Where(goqu.Ex{
- "col1": "a",
- "col2": 1,
-}).ScanStructs(&items)
-
-//Is the same as
-db.ScanStructs(&items, `SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?))`, "a", 1)
+Output:
+```
+UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
```
-
-
-
-### Database
-
-The Database also allows you to execute queries but expects raw SQL to execute. The supported methods are
-
-* [`Exec`](http://godoc.org/github.com/doug-martin/goqu#Database.Exec)
-* [`Prepare`](http://godoc.org/github.com/doug-martin/goqu#Database.Prepare)
-* [`Query`](http://godoc.org/github.com/doug-martin/goqu#Database.Query)
-* [`QueryRow`](http://godoc.org/github.com/doug-martin/goqu#Database.QueryRow)
-* [`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanStructs)
-* [`ScanStruct`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanStruct)
-* [`ScanVals`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanVals)
-* [`ScanVal`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanVal)
-* [`Begin`](http://godoc.org/github.com/doug-martin/goqu#Database.Begin)
-
-
-### Transactions
-
-`goqu` has builtin support for transactions to make the use of the Datasets and querying seamless
```go
-tx, err := db.Begin()
-if err != nil{
- return err
+type item struct {
+ Address string `db:"address"`
+ Name string `db:"name" goqu:"skipupdate"`
}
-//use tx.From to get a dataset that will execute within this transaction
-update := tx.From("user").
- Where(goqu.Ex("password": nil}).
- Update(goqu.Record{"status": "inactive"})
-if _, err = update.Exec(); err != nil{
- if rErr := tx.Rollback(); rErr != nil{
- return rErr
- }
- return err
-}
-if err = tx.Commit(); err != nil{
- return err
-}
-return
+sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
```
-The [`TxDatabase`](http://godoc.org/github.com/doug-martin/goqu/#TxDatabase) also has all methods that the [`Database`](http://godoc.org/github.com/doug-martin/goqu/#Database) has along with
-
-* [`Commit`](http://godoc.org/github.com/doug-martin/goqu#TxDatabase.Commit)
-* [`Rollback`](http://godoc.org/github.com/doug-martin/goqu#TxDatabase.Rollback)
-* [`Wrap`](http://godoc.org/github.com/doug-martin/goqu#TxDatabase.Wrap)
-
-#### Wrap
-
-The [`TxDatabase.Wrap`](http://godoc.org/github.com/doug-martin/goqu/#TxDatabase.Wrap) is a convience method for automatically handling `COMMIT` and `ROLLBACK`
+Output:
+```
+UPDATE "items" SET "address"='111 Test Addr' []
+```
```go
-tx, err := db.Begin()
-if err != nil{
- return err
-}
-err = tx.Wrap(func() error{
- update := tx.From("user").
- Where(goqu.Ex("password": nil}).
- Update(goqu.Record{"status": "inactive"})
- return update.Exec()
-})
-//err will be the original error from the update statement, unless there was an error executing ROLLBACK
-if err != nil{
- return err
-}
+sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10}
+ }).ToSQL()
+fmt.Println(sql)
```
-
-## Logging
-
-To enable trace logging of SQL statements use the [`Database.Logger`](http://godoc.org/github.com/doug-martin/goqu/#Database.Logger) method to set your logger.
-
-**NOTE** The logger must implement the [`Logger`](http://godoc.org/github.com/doug-martin/goqu/#Logger) interface
-
-**NOTE** If you start a transaction using a database your set a logger on the transaction will inherit that logger automatically
-
-
-
-## Custom Dialects
-
-Dialects in goqu are the foundation of building the correct SQL for each DB dialect.
-
-### Dialect Options
-
-Most SQL dialects share a majority of their syntax, for this reason `goqu` has a [default set of dialect options]((http://godoc.org/github.com/doug-martin/goqu/#DefaultDialectOptions)) that can be used as a base for any new Dialect.
-
-When creating a new `SQLDialect` you just need to override the default values that are documented in [`SQLDialectOptions`](http://godoc.org/github.com/doug-martin/goqu/#SQLDialectOptions).
-
-Take a look at [`postgres`](./dialect/postgres/postgres.go), [`mysql`](./dialect/mysql/mysql.go) and [`sqlite3`](./dialect/sqlite3/sqlite3.go) for examples.
-
-### Creating a custom dialect
+Output:
+```
+UPDATE "test" SET "foo"='bar' WHERE ("a" > 10)
+```
-When creating a new dialect you must register it using [`RegisterDialect`](http://godoc.org/github.com/doug-martin/goqu/#RegisterDialect). This method requires 2 arguments.
+### Delete
-1. `dialect string` - The name of your dialect
-2. `opts SQLDialectOptions` - The custom options for your dialect
+See the [delete dataset](./docs/deleting.md) docs for more in depth examples
-For example you could create a custom dialect that replaced the default quote `'"'` with a backtick `
```go
-opts := goqu.DefaultDialectOptions()
-opts.QuoteRune = '`'
-goqu.RegisterDialect("custom-dialect", opts)
-
-dialect := goqu.Dialect("custom-dialect")
-
-ds := dialect.From("test")
+ds := goqu.Delete("items")
sql, args, _ := ds.ToSQL()
fmt.Println(sql, args)
```
+```go
+sql, _, _ := goqu.Delete("test").Where(goqu.Ex{
+ "c": nil
+ }).ToSQL()
+fmt.Println(sql)
+```
+
Output:
```
-SELECT * FROM `test` []
+DELETE FROM "test" WHERE ("c" IS NULL)
```
-For more examples look at [`postgres`](./dialect/postgres/postgres.go), [`mysql`](./dialect/mysql/mysql.go) and [`sqlite3`](./dialect/sqlite3/sqlite3.go) for examples.
-
-
-## Migrating Between Versions
-
-### ` `ToSQL`
- * `ToInsertSql` -> `ToInsertSQL`
- * `ToUpdateSql` -> `ToUpdateSQL`
- * `ToDeleteSql` -> `ToDeleteSQL`
- * `ToTruncateSql` -> `ToTruncateSQL`
-* Abstracted out `dialect_options` from the adapter to make the dialect self contained.
- * This also removed the `dataset<->adapter` co dependency making the dialect self contained.
- * Added new dialect options to specify the order than SQL statements are built.
-* Refactored the `goqu.I` method.
- * Added new `goqu.S`, `goqu.T` and `goqu.C` methods to clarify why type of identifier you are using.
- * `goqu.I` should only be used when you have a qualified identifier (e.g. `goqu.I("my_schema.my_table.my_col")
-* Added new `goqu.Dialect` method to make using `goqu` as an SQL builder easier.
-
-
## Contributions
@@ -1094,3 +305,5 @@ GO_VERSION=latest docker-compose run goqu
+
+
diff --git a/database.go b/database.go
index e58d6e0f..d4916538 100644
--- a/database.go
+++ b/database.go
@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
- "github.com/doug-martin/goqu/v7/exec"
+ "github.com/doug-martin/goqu/v8/exec"
)
type (
@@ -40,8 +40,8 @@ type (
// import (
// "database/sql"
// "fmt"
-// "github.com/doug-martin/goqu/v7"
-// _ "github.com/doug-martin/goqu/v7/adapters/postgres"
+// "github.com/doug-martin/goqu/v8"
+// _ "github.com/doug-martin/goqu/v8/adapters/postgres"
// _ "github.com/lib/pq"
// )
//
@@ -107,10 +107,30 @@ func (d *Database) WithTx(fn func(*TxDatabase) error) error {
// fmt.Printf("%+v", ids)
//
// from...: Sources for you dataset, could be table names (strings), a goqu.Literal or another goqu.Dataset
-func (d *Database) From(from ...interface{}) *Dataset {
+func (d *Database) From(from ...interface{}) *SelectDataset {
return newDataset(d.dialect, d.queryFactory()).From(from...)
}
+func (d *Database) Select(cols ...interface{}) *SelectDataset {
+ return newDataset(d.dialect, d.queryFactory()).Select(cols...)
+}
+
+func (d *Database) Update(table interface{}) *UpdateDataset {
+ return newUpdateDataset(d.dialect, d.queryFactory()).Table(table)
+}
+
+func (d *Database) Insert(table interface{}) *InsertDataset {
+ return newInsertDataset(d.dialect, d.queryFactory()).Into(table)
+}
+
+func (d *Database) Delete(table interface{}) *DeleteDataset {
+ return newDeleteDataset(d.dialect, d.queryFactory()).From(table)
+}
+
+func (d *Database) Truncate(table ...interface{}) *TruncateDataset {
+ return newTruncateDataset(d.dialect, d.queryFactory()).Table(table...)
+}
+
// Sets the logger for to use when logging queries
func (d *Database) Logger(logger Logger) {
d.logger = logger
@@ -437,10 +457,30 @@ func (td *TxDatabase) Dialect() string {
}
// Creates a new Dataset for querying a Database.
-func (td *TxDatabase) From(cols ...interface{}) *Dataset {
+func (td *TxDatabase) From(cols ...interface{}) *SelectDataset {
return newDataset(td.dialect, td.queryFactory()).From(cols...)
}
+func (td *TxDatabase) Select(cols ...interface{}) *SelectDataset {
+ return newDataset(td.dialect, td.queryFactory()).Select(cols...)
+}
+
+func (td *TxDatabase) Update(table interface{}) *UpdateDataset {
+ return newUpdateDataset(td.dialect, td.queryFactory()).Table(table)
+}
+
+func (td *TxDatabase) Insert(table interface{}) *InsertDataset {
+ return newInsertDataset(td.dialect, td.queryFactory()).Into(table)
+}
+
+func (td *TxDatabase) Delete(table interface{}) *DeleteDataset {
+ return newDeleteDataset(td.dialect, td.queryFactory()).From(table)
+}
+
+func (td *TxDatabase) Truncate(table ...interface{}) *TruncateDataset {
+ return newTruncateDataset(td.dialect, td.queryFactory()).Table(table...)
+}
+
// Sets the logger
func (td *TxDatabase) Logger(logger Logger) {
td.logger = logger
diff --git a/database_example_test.go b/database_example_test.go
index 627b7437..0dced733 100644
--- a/database_example_test.go
+++ b/database_example_test.go
@@ -6,7 +6,7 @@ import (
"fmt"
"time"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
)
func ExampleDatabase_Begin() {
@@ -18,10 +18,11 @@ func ExampleDatabase_Begin() {
}
// use tx.From to get a dataset that will execute within this transaction
- update := tx.From("goqu_user").
+ update := tx.Update("goqu_user").
+ Set(goqu.Record{"last_name": "Ucon"}).
Where(goqu.Ex{"last_name": "Yukon"}).
Returning("id").
- Update(goqu.Record{"last_name": "Ucon"})
+ Executor()
var ids []int64
if err := update.ScanVals(&ids); err != nil {
@@ -51,10 +52,11 @@ func ExampleDatabase_BeginTx() {
}
// use tx.From to get a dataset that will execute within this transaction
- update := tx.From("goqu_user").
+ update := tx.Update("goqu_user").
+ Set(goqu.Record{"last_name": "Ucon"}).
Where(goqu.Ex{"last_name": "Yukon"}).
Returning("id").
- Update(goqu.Record{"last_name": "Ucon"})
+ Executor()
var ids []int64
if err := update.ScanVals(&ids); err != nil {
@@ -79,10 +81,11 @@ func ExampleDatabase_WithTx() {
var ids []int64
if err := db.WithTx(func(tx *goqu.TxDatabase) error {
// use tx.From to get a dataset that will execute within this transaction
- update := tx.From("goqu_user").
+ update := tx.Update("goqu_user").
Where(goqu.Ex{"last_name": "Yukon"}).
Returning("id").
- Update(goqu.Record{"last_name": "Ucon"})
+ Set(goqu.Record{"last_name": "Ucon"}).
+ Executor()
return update.ScanVals(&ids)
}); err != nil {
diff --git a/database_test.go b/database_test.go
index 036e387e..57e33eaa 100644
--- a/database_test.go
+++ b/database_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"github.com/DATA-DOG/go-sqlmock"
- "github.com/doug-martin/goqu/v7/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
diff --git a/dataset.go b/dataset.go
deleted file mode 100644
index b16b0d20..00000000
--- a/dataset.go
+++ /dev/null
@@ -1,776 +0,0 @@
-package goqu
-
-import (
- "context"
- "fmt"
-
- "github.com/doug-martin/goqu/v7/exec"
- "github.com/doug-martin/goqu/v7/exp"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/sb"
-)
-
-type (
- // A Dataset is used to build up an SQL statement, each method returns a copy of the current Dataset with options
- // added to it.
- //
- // Once done building up your Dataset you can either call an action method on it to execute the statement or use
- // one of the SQL generation methods.
- //
- // Common SQL clauses are represented as methods on the Dataset (e.g. Where, From, Select, Limit...)
- // * ToSQL() - Returns a SELECT statement
- // * ToUpdateSQL() - Returns an UPDATE statement
- // * ToInsertSQL(rows ...interface{}) - Returns an INSERT statement
- // * ToDeleteSQL() - Returns a DELETE statement
- // * ToTruncateSQL() - Returns a TRUNCATE statement.
- //
- // Each SQL generation method returns an interpolated statement. Without interpolation each SQL statement could
- // cause two calls to the database:
- // 1. Prepare the statement
- // 2. Execute the statement with arguments
- //
- // Instead with interpolation the database just executes the statement
- // sql, _, err := goqu.From("test").Where(goqu.C("a").Eq(10)).ToSQL()
- // fmt.Println(sql)
- //
- // // Output:
- // // SELECT * FROM "test" WHERE "a" = 10
- //
- // Sometimes you might want to generated a prepared statement in which case you would use one of the "Prepared"
- // method on the dataset
- // sql, args, err := From("test").Prepared(true).Where(I("a").Eq(10)).ToSQL()
- // fmt.Println(sql, args)
- //
- // // Output:
- // // SELECT * FROM "test" WHERE "a" = ? [10]
- //
- // A Dataset can also execute statements directly. By calling:
- //
- // * ScanStructs(i interface{}) - Scans returned rows into a slice of structs
- // * ScanStruct(i interface{}) - Scans a single rom into a struct, if no struct is found this method will return
- // false
- // * ScanVals(i interface{}) - Scans rows of one columns into a slice of primitive values
- // * ScanVal(i interface{}) - Scans a single row of one column into a primitive value
- // * Count() - Returns a count of rows
- // * Pluck(i interface{}, col string) - Retrives a columns from rows and scans the resules into a slice of
- // primitive values.
- //
- // Update, Delete, and Insert return an CrudExec struct which can be used to scan values or just execute the
- // statement. You might
- // use the scan methods if the database supports return values. For example
- // UPDATE "items" SET updated = NOW RETURNING "items".*
- // Could be executed with ScanStructs.
- Dataset struct {
- dialect SQLDialect
- clauses exp.Clauses
- isPrepared bool
- queryFactory exec.QueryFactory
- }
-)
-
-var (
- errQueryFactoryNotFoundError = errors.New(
- "unable to execute query did you use goqu.Database#From to create the dataset",
- )
-)
-
-// used internally by database to create a database with a specific adapter
-func newDataset(d string, queryFactory exec.QueryFactory) *Dataset {
- return &Dataset{
- clauses: exp.NewClauses(),
- dialect: GetDialect(d),
- queryFactory: queryFactory,
- }
-}
-
-func From(table ...interface{}) *Dataset {
- return newDataset("default", nil).From(table...)
-}
-
-// Sets the adapter used to serialize values and create the SQL statement
-func (d *Dataset) WithDialect(dl string) *Dataset {
- ds := d.copy(d.GetClauses())
- ds.dialect = GetDialect(dl)
- return ds
-}
-
-// Set the parameter interpolation behavior. See examples
-//
-// prepared: If true the dataset WILL NOT interpolate the parameters.
-func (d *Dataset) Prepared(prepared bool) *Dataset {
- ret := d.copy(d.clauses)
- ret.isPrepared = prepared
- return ret
-}
-
-func (d *Dataset) IsPrepared() bool {
- return d.isPrepared
-}
-
-// Returns the current adapter on the dataset
-func (d *Dataset) Dialect() SQLDialect {
- return d.dialect
-}
-
-// Returns the current adapter on the dataset
-func (d *Dataset) SetDialect(dialect SQLDialect) *Dataset {
- cd := d.copy(d.GetClauses())
- cd.dialect = dialect
- return cd
-}
-
-func (d *Dataset) Expression() exp.Expression {
- return d
-}
-
-// Clones the dataset
-func (d *Dataset) Clone() exp.Expression {
- return d.copy(d.clauses)
-}
-
-// Returns the current clauses on the dataset.
-func (d *Dataset) GetClauses() exp.Clauses {
- return d.clauses
-}
-
-// used interally to copy the dataset
-func (d *Dataset) copy(clauses exp.Clauses) *Dataset {
- return &Dataset{
- dialect: d.dialect,
- clauses: clauses,
- isPrepared: d.isPrepared,
- queryFactory: d.queryFactory,
- }
-}
-
-// Creates a WITH clause for a common table expression (CTE).
-//
-// The name will be available to SELECT from in the associated query; and can optionally
-// contain a list of column names "name(col1, col2, col3)".
-//
-// The name will refer to the results of the specified subquery.
-func (d *Dataset) With(name string, subquery exp.Expression) *Dataset {
- return d.copy(d.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
-}
-
-// Creates a WITH RECURSIVE clause for a common table expression (CTE)
-//
-// The name will be available to SELECT from in the associated query; and must
-// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
-//
-// The name will refer to the results of the specified subquery. The subquery for
-// a recursive query will always end with a UNION or UNION ALL with a clause that
-// refers to the CTE by name.
-func (d *Dataset) WithRecursive(name string, subquery exp.Expression) *Dataset {
- return d.copy(d.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
-}
-
-// Adds columns to the SELECT clause. See examples
-// You can pass in the following.
-// string: Will automatically be turned into an identifier
-// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
-// column name.
-// LiteralExpression: (See Literal) Will use the literal SQL
-// SQLFunction: (See Func, MIN, MAX, COUNT....)
-// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
-// See examples
-func (d *Dataset) Select(selects ...interface{}) *Dataset {
- return d.copy(d.clauses.SetSelect(exp.NewColumnListExpression(selects...)))
-}
-
-// Adds columns to the SELECT DISTINCT clause. See examples
-// You can pass in the following.
-// string: Will automatically be turned into an identifier
-// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
-// column name.
-// LiteralExpression: (See Literal) Will use the literal SQL
-// SQLFunction: (See Func, MIN, MAX, COUNT....)
-// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
-// See examples
-func (d *Dataset) SelectDistinct(selects ...interface{}) *Dataset {
- return d.copy(d.clauses.SetSelectDistinct(exp.NewColumnListExpression(selects...)))
-}
-
-// Resets to SELECT *. If the SelectDistinct was used the returned Dataset will have the the dataset set to SELECT *.
-// See examples.
-func (d *Dataset) ClearSelect() *Dataset {
- return d.copy(d.clauses.SetSelect(exp.NewColumnListExpression(exp.Star())))
-}
-
-// Adds columns to the SELECT clause. See examples
-// You can pass in the following.
-// string: Will automatically be turned into an identifier
-// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
-// column name.
-// LiteralExpression: (See Literal) Will use the literal SQL
-// SQLFunction: (See Func, MIN, MAX, COUNT....)
-func (d *Dataset) SelectAppend(selects ...interface{}) *Dataset {
- return d.copy(d.clauses.SelectAppend(exp.NewColumnListExpression(selects...)))
-}
-
-// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
-// You can pass in the following.
-// string: Will automatically be turned into an identifier
-// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
-// LiteralExpression: (See Literal) Will use the literal SQL
-func (d *Dataset) From(from ...interface{}) *Dataset {
- var sources []interface{}
- numSources := 0
- for _, source := range from {
- if sd, ok := source.(*Dataset); ok && !sd.clauses.HasAlias() {
- numSources++
- sources = append(sources, sd.As(fmt.Sprintf("t%d", numSources)))
- } else {
- sources = append(sources, source)
- }
- }
- return d.copy(d.clauses.SetFrom(exp.NewColumnListExpression(sources...)))
-}
-
-// Returns a new Dataset with the current one as an source. If the current Dataset is not aliased (See Dataset#As) then
-// it will automatically be aliased. See examples.
-func (d *Dataset) FromSelf() *Dataset {
- builder := Dataset{
- dialect: d.dialect,
- clauses: exp.NewClauses(),
- }
- return builder.From(d)
-
-}
-
-// Alias to InnerJoin. See examples.
-func (d *Dataset) Join(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.InnerJoin(table, condition)
-}
-
-// Adds an INNER JOIN clause. See examples.
-func (d *Dataset) InnerJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.InnerJoinType, table, condition))
-}
-
-// Adds a FULL OUTER JOIN clause. See examples.
-func (d *Dataset) FullOuterJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.FullOuterJoinType, table, condition))
-}
-
-// Adds a RIGHT OUTER JOIN clause. See examples.
-func (d *Dataset) RightOuterJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.RightOuterJoinType, table, condition))
-}
-
-// Adds a LEFT OUTER JOIN clause. See examples.
-func (d *Dataset) LeftOuterJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.LeftOuterJoinType, table, condition))
-}
-
-// Adds a FULL JOIN clause. See examples.
-func (d *Dataset) FullJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.FullJoinType, table, condition))
-}
-
-// Adds a RIGHT JOIN clause. See examples.
-func (d *Dataset) RightJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.RightJoinType, table, condition))
-}
-
-// Adds a LEFT JOIN clause. See examples.
-func (d *Dataset) LeftJoin(table exp.Expression, condition exp.JoinCondition) *Dataset {
- return d.joinTable(exp.NewConditionedJoinExpression(exp.LeftJoinType, table, condition))
-}
-
-// Adds a NATURAL JOIN clause. See examples.
-func (d *Dataset) NaturalJoin(table exp.Expression) *Dataset {
- return d.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, table))
-}
-
-// Adds a NATURAL LEFT JOIN clause. See examples.
-func (d *Dataset) NaturalLeftJoin(table exp.Expression) *Dataset {
- return d.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalLeftJoinType, table))
-}
-
-// Adds a NATURAL RIGHT JOIN clause. See examples.
-func (d *Dataset) NaturalRightJoin(table exp.Expression) *Dataset {
- return d.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalRightJoinType, table))
-}
-
-// Adds a NATURAL FULL JOIN clause. See examples.
-func (d *Dataset) NaturalFullJoin(table exp.Expression) *Dataset {
- return d.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalFullJoinType, table))
-}
-
-// Adds a CROSS JOIN clause. See examples.
-func (d *Dataset) CrossJoin(table exp.Expression) *Dataset {
- return d.joinTable(exp.NewUnConditionedJoinExpression(exp.CrossJoinType, table))
-}
-
-// Joins this Datasets table with another
-func (d *Dataset) joinTable(join exp.JoinExpression) *Dataset {
- return d.copy(d.clauses.JoinsAppend(join))
-}
-
-// Adds a WHERE clause. See examples.
-func (d *Dataset) Where(expressions ...exp.Expression) *Dataset {
- return d.copy(d.clauses.WhereAppend(expressions...))
-}
-
-// Removes the WHERE clause. See examples.
-func (d *Dataset) ClearWhere() *Dataset {
- return d.copy(d.clauses.ClearWhere())
-}
-
-// Adds a FOR UPDATE clause. See examples.
-func (d *Dataset) ForUpdate(waitOption exp.WaitOption) *Dataset {
- return d.withLock(exp.ForUpdate, waitOption)
-}
-
-// Adds a FOR NO KEY UPDATE clause. See examples.
-func (d *Dataset) ForNoKeyUpdate(waitOption exp.WaitOption) *Dataset {
- return d.withLock(exp.ForNoKeyUpdate, waitOption)
-}
-
-// Adds a FOR KEY SHARE clause. See examples.
-func (d *Dataset) ForKeyShare(waitOption exp.WaitOption) *Dataset {
- return d.withLock(exp.ForKeyShare, waitOption)
-}
-
-// Adds a FOR SHARE clause. See examples.
-func (d *Dataset) ForShare(waitOption exp.WaitOption) *Dataset {
- return d.withLock(exp.ForShare, waitOption)
-}
-
-func (d *Dataset) withLock(strength exp.LockStrength, option exp.WaitOption) *Dataset {
- return d.copy(d.clauses.SetLock(exp.NewLock(strength, option)))
-}
-
-// Adds a GROUP BY clause. See examples.
-func (d *Dataset) GroupBy(groupBy ...interface{}) *Dataset {
- return d.copy(d.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...)))
-}
-
-// Adds a HAVING clause. See examples.
-func (d *Dataset) Having(expressions ...exp.Expression) *Dataset {
- return d.copy(d.clauses.HavingAppend(expressions...))
-}
-
-// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
-func (d *Dataset) Order(order ...exp.OrderedExpression) *Dataset {
- return d.copy(d.clauses.SetOrder(order...))
-}
-
-// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
-// calling Order. See examples.
-func (d *Dataset) OrderAppend(order ...exp.OrderedExpression) *Dataset {
- return d.copy(d.clauses.OrderAppend(order...))
-}
-
-// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
-// calling Order. See examples.
-func (d *Dataset) OrderPrepend(order ...exp.OrderedExpression) *Dataset {
- return d.copy(d.clauses.OrderPrepend(order...))
-}
-
-// Removes the ORDER BY clause. See examples.
-func (d *Dataset) ClearOrder() *Dataset {
- return d.copy(d.clauses.ClearOrder())
-}
-
-// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
-func (d *Dataset) Limit(limit uint) *Dataset {
- if limit > 0 {
- return d.copy(d.clauses.SetLimit(limit))
- }
- return d.copy(d.clauses.ClearLimit())
-}
-
-// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
-func (d *Dataset) LimitAll() *Dataset {
- return d.copy(d.clauses.SetLimit(L("ALL")))
-}
-
-// Removes the LIMIT clause.
-func (d *Dataset) ClearLimit() *Dataset {
- return d.copy(d.clauses.ClearLimit())
-}
-
-// Adds an OFFSET clause. If the OFFSET is currently set it replaces it. See examples.
-func (d *Dataset) Offset(offset uint) *Dataset {
- return d.copy(d.clauses.SetOffset(offset))
-}
-
-// Removes the OFFSET clause from the Dataset
-func (d *Dataset) ClearOffset() *Dataset {
- return d.copy(d.clauses.ClearOffset())
-}
-
-// Creates an UNION statement with another dataset.
-// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
-// See examples.
-func (d *Dataset) Union(other *Dataset) *Dataset {
- return d.withCompound(exp.UnionCompoundType, other.CompoundFromSelf())
-}
-
-// Creates an UNION ALL statement with another dataset.
-// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
-// See examples.
-func (d *Dataset) UnionAll(other *Dataset) *Dataset {
- return d.withCompound(exp.UnionAllCompoundType, other.CompoundFromSelf())
-}
-
-// Creates an INTERSECT statement with another dataset.
-// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
-// See examples.
-func (d *Dataset) Intersect(other *Dataset) *Dataset {
- return d.withCompound(exp.IntersectCompoundType, other.CompoundFromSelf())
-}
-
-// Creates an INTERSECT ALL statement with another dataset.
-// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
-// See examples.
-func (d *Dataset) IntersectAll(other *Dataset) *Dataset {
- return d.withCompound(exp.IntersectAllCompoundType, other.CompoundFromSelf())
-}
-
-func (d *Dataset) withCompound(ct exp.CompoundType, other exp.AppendableExpression) *Dataset {
- ce := exp.NewCompoundExpression(ct, other)
- ret := d.CompoundFromSelf()
- ret.clauses = ret.clauses.CompoundsAppend(ce)
- return ret
-}
-
-// Used internally to determine if the dataset needs to use iteself as a source.
-// If the dataset has an order or limit it will select from itself
-func (d *Dataset) CompoundFromSelf() *Dataset {
- if d.clauses.HasOrder() || d.clauses.HasLimit() {
- return d.FromSelf()
- }
- return d.copy(d.clauses)
-}
-
-// Adds a RETURNING clause to the dataset if the adapter supports it. Typically used for INSERT, UPDATE or DELETE.
-// See examples.
-func (d *Dataset) Returning(returning ...interface{}) *Dataset {
- return d.copy(d.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
-}
-
-// Sets the alias for this dataset. This is typically used when using a Dataset as a subselect. See examples.
-func (d *Dataset) As(alias string) *Dataset {
- return d.copy(d.clauses.SetAlias(T(alias)))
-}
-
-// Generates a SELECT sql statement, if Prepared has been called with true then the parameters will not be interpolated.
-// See examples.
-//
-// Errors:
-// * There is an error generating the SQL
-func (d *Dataset) ToSQL() (sql string, params []interface{}, err error) {
- return d.selectSQLBuilder().ToSQL()
-}
-
-// Appends this Dataset's SELECT statement to the SQLBuilder
-// This is used internally for sub-selects by the dialect
-func (d *Dataset) AppendSQL(b sb.SQLBuilder) {
- d.dialect.ToSelectSQL(b, d.GetClauses())
-}
-
-// 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`
-// type Item struct{
-// Id uint32 `db:"id" goqu:"skipinsert"`
-// Name string `db:"name"`
-// }
-//
-// rows: variable number arguments of either map[string]interface, Record, struct, or a single slice argument of the
-// accepted types.
-//
-// Errors:
-// * There is no FROM clause
-// * Different row types passed in, all rows must be of the same type
-// * Maps with different numbers of K/V pairs
-// * Rows of different lengths, (i.e. (Record{"name": "a"}, Record{"name": "a", "age": 10})
-// * Error generating SQL
-func (d *Dataset) ToInsertSQL(rows ...interface{}) (sql string, params []interface{}, err error) {
- return d.toInsertSQL(nil, rows...)
-}
-
-// Generates the default INSERT IGNORE/ INSERT ... ON CONFLICT DO NOTHING statement. If Prepared has been called with
-// true then the statement will not be interpolated. See examples.
-//
-// c: ConflictExpression action. Can be DoNothing/Ignore or DoUpdate/DoUpdateWhere.
-// rows: variable number arguments of either map[string]interface, Record, struct, or a single slice argument of the
-// accepted types.
-//
-// Errors:
-// * There is no FROM clause
-// * Different row types passed in, all rows must be of the same type
-// * Maps with different numbers of K/V pairs
-// * Rows of different lengths, (i.e. (Record{"name": "a"}, Record{"name": "a", "age": 10})
-// * Error generating SQL
-func (d *Dataset) ToInsertIgnoreSQL(rows ...interface{}) (sql string, params []interface{}, err error) {
- return d.toInsertSQL(DoNothing(), rows...)
-}
-
-// Generates the INSERT [IGNORE] ... ON CONFLICT/DUPLICATE KEY. If Prepared has been called with true then the statement
-// will not be interpolated. See examples.
-//
-// rows: variable number arguments of either map[string]interface, Record, struct, or a single slice argument of the
-// accepted types.
-//
-// Errors:
-// * There is no FROM clause
-// * Different row types passed in, all rows must be of the same type
-// * Maps with different numbers of K/V pairs
-// * Rows of different lengths, (i.e. (Record{"name": "a"}, Record{"name": "a", "age": 10})
-// * Error generating SQL
-func (d *Dataset) ToInsertConflictSQL(o exp.ConflictExpression, rows ...interface{}) (sql string, params []interface{}, err error) {
- return d.toInsertSQL(o, rows)
-}
-
-// Generates an UPDATE statement. If `Prepared` has been called with true then the statement will not be interpolated.
-// When using structs you may specify a column to be skipped in the update, (e.g. created) by specifying a goqu tag with `skipupdate`
-// type Item struct{
-// Id uint32 `db:"id"
-// Created time.Time `db:"created" goqu:"skipupdate"`
-// Name string `db:"name"`
-// }
-//
-// update: can either be a a map[string]interface{}, Record or a struct
-//
-// Errors:
-// * The update is not a of type struct, Record, or map[string]interface{}
-// * The update statement has no FROM clause
-// * There is an error generating the SQL
-func (d *Dataset) ToUpdateSQL(update interface{}) (sql string, params []interface{}, err error) {
- return d.updateSQLBuilder(update).ToSQL()
-}
-
-// Generates a DELETE statement, if Prepared has been called with true then the statement will not be interpolated. See examples.
-//
-// isPrepared: Set to true to true to ensure values are NOT interpolated
-//
-// Errors:
-// * There is no FROM clause
-// * Error generating SQL
-func (d *Dataset) ToDeleteSQL() (sql string, params []interface{}, err error) {
- return d.deleteSQLBuilder().ToSQL()
-}
-
-// Generates the default TRUNCATE statement. See examples.
-//
-// Errors:
-// * There is no FROM clause
-// * Error generating SQL
-func (d *Dataset) ToTruncateSQL() (sql string, params []interface{}, err error) {
- return d.ToTruncateWithOptsSQL(exp.TruncateOptions{})
-}
-
-// Generates the default TRUNCATE statement with the specified options. See examples.
-//
-// opts: Options to use when generating the TRUNCATE statement
-//
-// Errors:
-// * There is no FROM clause
-// * Error generating SQL
-func (d *Dataset) ToTruncateWithOptsSQL(opts exp.TruncateOptions) (sql string, params []interface{}, err error) {
- return d.truncateSQLBuilder(opts).ToSQL()
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanStructs to scan the results into a slice of structs.
-//
-// ScanStructs will only select the columns that can be scanned in to the struct unless you have explicitly selected
-// certain columns. See examples.
-//
-// i: A pointer to a slice of structs
-func (d *Dataset) ScanStructs(i interface{}) error {
- return d.ScanStructsContext(context.Background(), i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanStructsContext to scan the results into a slice of
-// structs.
-//
-// ScanStructsContext will only select the columns that can be scanned in to the struct unless you have explicitly
-// selected certain columns. See examples.
-//
-// i: A pointer to a slice of structs
-func (d *Dataset) ScanStructsContext(ctx context.Context, i interface{}) error {
- if d.queryFactory == nil {
- return errQueryFactoryNotFoundError
- }
- ds := d
- if d.GetClauses().IsDefaultSelect() {
- ds = d.Select(i)
- }
- return d.queryFactory.FromSQLBuilder(ds.selectSQLBuilder()).ScanStructsContext(ctx, i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanStruct to scan the result into a slice of structs
-//
-// ScanStruct will only select the columns that can be scanned in to the struct unless you have explicitly selected
-// certain columns. See examples.
-//
-// i: A pointer to a structs
-func (d *Dataset) ScanStruct(i interface{}) (bool, error) {
- return d.ScanStructContext(context.Background(), i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanStructContext to scan the result into a slice of structs
-//
-// ScanStructContext will only select the columns that can be scanned in to the struct unless you have explicitly
-// selected certain columns. See examples.
-//
-// i: A pointer to a structs
-func (d *Dataset) ScanStructContext(ctx context.Context, i interface{}) (bool, error) {
- if d.queryFactory == nil {
- return false, errQueryFactoryNotFoundError
- }
- ds := d
- if d.GetClauses().IsDefaultSelect() {
- ds = d.Select(i)
- }
- return d.queryFactory.FromSQLBuilder(ds.Limit(1).selectSQLBuilder()).ScanStructContext(ctx, i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanVals to scan the results into a slice of primitive values
-//
-// i: A pointer to a slice of primitive values
-func (d *Dataset) ScanVals(i interface{}) error {
- return d.ScanValsContext(context.Background(), i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanValsContext to scan the results into a slice of primitive
-// values
-//
-// i: A pointer to a slice of primitive values
-func (d *Dataset) ScanValsContext(ctx context.Context, i interface{}) error {
- if d.queryFactory == nil {
- return errQueryFactoryNotFoundError
- }
- return d.queryFactory.FromSQLBuilder(d.selectSQLBuilder()).ScanValsContext(ctx, i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanVal to scan the result into a primitive value
-//
-// i: A pointer to a primitive value
-func (d *Dataset) ScanVal(i interface{}) (bool, error) {
- return d.ScanValContext(context.Background(), i)
-}
-
-// Generates the SELECT sql for this dataset and uses Exec#ScanValContext to scan the result into a primitive value
-//
-// i: A pointer to a primitive value
-func (d *Dataset) ScanValContext(ctx context.Context, i interface{}) (bool, error) {
- if d.queryFactory == nil {
- return false, errQueryFactoryNotFoundError
- }
- b := d.Limit(1).selectSQLBuilder()
- return d.queryFactory.FromSQLBuilder(b).ScanValContext(ctx, i)
-}
-
-// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanVal to scan the result into an int64.
-func (d *Dataset) Count() (int64, error) {
- return d.CountContext(context.Background())
-}
-
-// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanValContext to scan the result into an int64.
-func (d *Dataset) CountContext(ctx context.Context) (int64, error) {
- var count int64
- _, err := d.Select(COUNT(Star()).As("count")).ScanValContext(ctx, &count)
- return count, err
-}
-
-// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanVals to scan the result into a slice
-// of primitive values.
-//
-// i: A slice of primitive values
-//
-// col: The column to select when generative the SQL
-func (d *Dataset) Pluck(i interface{}, col string) error {
- return d.PluckContext(context.Background(), i, col)
-}
-
-// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanValsContext to scan the result into a
-// slice of primitive values.
-//
-// i: A slice of primitive values
-//
-// col: The column to select when generative the SQL
-func (d *Dataset) PluckContext(ctx context.Context, i interface{}, col string) error {
- return d.Select(col).ScanValsContext(ctx, i)
-}
-
-// Generates the UPDATE sql, and returns an Exec struct with the sql set to the UPDATE statement
-// db.From("test").Update(Record{"name":"Bob", update: time.Now()}).Exec()
-//
-// See Dataset#ToUpdateSQL for arguments
-func (d *Dataset) Update(i interface{}) exec.QueryExecutor {
- return d.queryFactory.FromSQLBuilder(d.updateSQLBuilder(i))
-}
-
-// Generates the INSERT sql, and returns an Exec struct with the sql set to the INSERT statement
-// db.From("test").Insert(Record{"name":"Bob"}).Exec()
-//
-// See Dataset#ToInsertSQL for arguments
-func (d *Dataset) Insert(i ...interface{}) exec.QueryExecutor {
- return d.InsertConflict(nil, i...)
-}
-
-// Generates the INSERT IGNORE (mysql) or INSERT ... ON CONFLICT DO NOTHING (postgres) and returns an Exec struct.
-// db.From("test").InsertIgnore(DoNothing(), Record{"name":"Bob"}).Exec()
-//
-// See Dataset#ToInsertConflictSQL for arguments
-func (d *Dataset) InsertIgnore(i ...interface{}) exec.QueryExecutor {
- return d.InsertConflict(DoNothing(), i...)
-}
-
-// Generates the INSERT sql with (ON CONFLICT/ON DUPLICATE KEY) clause, and returns an Exec struct with the sql set to
-// the INSERT statement
-// db.From("test").InsertConflict(DoNothing(), Record{"name":"Bob"}).Exec()
-//
-// See Dataset#Upsert for arguments
-func (d *Dataset) InsertConflict(c exp.ConflictExpression, i ...interface{}) exec.QueryExecutor {
- return d.queryFactory.FromSQLBuilder(d.insertSQLBuilder(c, i...))
-}
-
-// Generates the DELETE sql, and returns an Exec struct with the sql set to the DELETE statement
-// db.From("test").Where(I("id").Gt(10)).Exec()
-func (d *Dataset) Delete() exec.QueryExecutor {
- return d.queryFactory.FromSQLBuilder(d.deleteSQLBuilder())
-}
-
-func (d *Dataset) selectSQLBuilder() sb.SQLBuilder {
- buf := sb.NewSQLBuilder(d.isPrepared)
- d.dialect.ToSelectSQL(buf, d.GetClauses())
- return buf
-}
-
-func (d *Dataset) toInsertSQL(ce exp.ConflictExpression, rows ...interface{}) (sql string, params []interface{}, err error) {
- return d.insertSQLBuilder(ce, rows...).ToSQL()
-}
-
-func (d *Dataset) insertSQLBuilder(ce exp.ConflictExpression, rows ...interface{}) sb.SQLBuilder {
- buf := sb.NewSQLBuilder(d.isPrepared)
- ie, err := exp.NewInsertExpression(rows...)
- if err != nil {
- return buf.SetError(err)
- }
- d.dialect.ToInsertSQL(buf, d.clauses, ie.SetOnConflict(ce))
- return buf
-}
-
-func (d *Dataset) updateSQLBuilder(update interface{}) sb.SQLBuilder {
- buf := sb.NewSQLBuilder(d.isPrepared)
- d.dialect.ToUpdateSQL(buf, d.clauses, update)
- return buf
-}
-
-func (d *Dataset) deleteSQLBuilder() sb.SQLBuilder {
- buf := sb.NewSQLBuilder(d.isPrepared)
- d.dialect.ToDeleteSQL(buf, d.clauses)
- return buf
-}
-
-func (d *Dataset) truncateSQLBuilder(opts exp.TruncateOptions) sb.SQLBuilder {
- buf := sb.NewSQLBuilder(d.isPrepared)
- d.dialect.ToTruncateSQL(buf, d.clauses, opts)
- return buf
-}
diff --git a/dataset_query_example_test.go b/dataset_query_example_test.go
deleted file mode 100644
index 84a81b45..00000000
--- a/dataset_query_example_test.go
+++ /dev/null
@@ -1,374 +0,0 @@
-package goqu_test
-
-import (
- "database/sql"
- "fmt"
- "os"
- "time"
-
- "github.com/doug-martin/goqu/v7"
- _ "github.com/doug-martin/goqu/v7/dialect/postgres"
- "github.com/lib/pq"
-)
-
-const schema = `
- DROP TABLE IF EXISTS "goqu_user";
- CREATE TABLE "goqu_user" (
- "id" SERIAL PRIMARY KEY NOT NULL,
- "first_name" VARCHAR(45) NOT NULL,
- "last_name" VARCHAR(45) NOT NULL,
- "created" TIMESTAMP NOT NULL DEFAULT now()
- );
- INSERT INTO "goqu_user" ("first_name", "last_name") VALUES
- ('Bob', 'Yukon'),
- ('Sally', 'Yukon'),
- ('Vinita', 'Yukon'),
- ('John', 'Doe')
- `
-
-const defaultDbURI = "postgres://postgres:@localhost:5435/goqupostgres?sslmode=disable"
-
-var goquDb *goqu.Database
-
-func getDb() *goqu.Database {
- if goquDb == nil {
- dbURI := os.Getenv("PG_URI")
- if dbURI == "" {
- dbURI = defaultDbURI
- }
- uri, err := pq.ParseURL(dbURI)
- if err != nil {
- panic(err)
- }
- pdb, err := sql.Open("postgres", uri)
- if err != nil {
- panic(err)
- }
- goquDb = goqu.New("postgres", pdb)
- }
- // reset the db
- if _, err := goquDb.Exec(schema); err != nil {
- panic(err)
- }
- return goquDb
-}
-
-func ExampleDataset_ScanStructs() {
- type User struct {
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- }
- db := getDb()
- var users []User
- if err := db.From("goqu_user").ScanStructs(&users); err != nil {
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("\n%+v", users)
-
- users = users[0:0]
- if err := db.From("goqu_user").Select("first_name").ScanStructs(&users); err != nil {
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("\n%+v", users)
-
- // Output:
- // [{FirstName:Bob LastName:Yukon} {FirstName:Sally LastName:Yukon} {FirstName:Vinita LastName:Yukon} {FirstName:John LastName:Doe}]
- // [{FirstName:Bob LastName:} {FirstName:Sally LastName:} {FirstName:Vinita LastName:} {FirstName:John LastName:}]
-}
-
-func ExampleDataset_ScanStructs_prepared() {
- type User struct {
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- }
- db := getDb()
-
- ds := db.From("goqu_user").
- Prepared(true).
- Where(goqu.Ex{
- "last_name": "Yukon",
- })
-
- var users []User
- if err := ds.ScanStructs(&users); err != nil {
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("\n%+v", users)
-
- // Output:
- // [{FirstName:Bob LastName:Yukon} {FirstName:Sally LastName:Yukon} {FirstName:Vinita LastName:Yukon}]
-}
-
-func ExampleDataset_ScanStruct() {
- type User struct {
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- }
- db := getDb()
- findUserByName := func(name string) {
- var user User
- ds := db.From("goqu_user").Where(goqu.C("first_name").Eq(name))
- found, err := ds.ScanStruct(&user)
- switch {
- case err != nil:
- fmt.Println(err.Error())
- case !found:
- fmt.Printf("No user found for first_name %s\n", name)
- default:
- fmt.Printf("Found user: %+v\n", user)
- }
- }
-
- findUserByName("Bob")
- findUserByName("Zeb")
-
- // Output:
- // Found user: {FirstName:Bob LastName:Yukon}
- // No user found for first_name Zeb
-}
-
-func ExampleDataset_ScanVals() {
- var ids []int64
- if err := getDb().From("goqu_user").Select("id").ScanVals(&ids); err != nil {
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("UserIds = %+v", ids)
-
- // Output:
- // UserIds = [1 2 3 4]
-}
-
-func ExampleDataset_ScanVal() {
-
- db := getDb()
- findUserIDByName := func(name string) {
- var id int64
- ds := db.From("goqu_user").
- Select("id").
- Where(goqu.C("first_name").Eq(name))
-
- found, err := ds.ScanVal(&id)
- switch {
- case err != nil:
- fmt.Println(err.Error())
- case !found:
- fmt.Printf("No id found for user %s", name)
- default:
- fmt.Printf("\nFound userId: %+v\n", id)
- }
- }
-
- findUserIDByName("Bob")
- findUserIDByName("Zeb")
- // Output:
- // Found userId: 1
- // No id found for user Zeb
-}
-
-func ExampleDataset_Count() {
-
- if count, err := getDb().From("goqu_user").Count(); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Printf("\nCount:= %d", count)
- }
-
- // Output:
- // Count:= 4
-}
-
-func ExampleDataset_Pluck() {
- var lastNames []string
- if err := getDb().From("goqu_user").Pluck(&lastNames, "last_name"); err != nil {
- fmt.Println(err.Error())
- return
- }
- fmt.Printf("LastNames := %+v", lastNames)
-
- // Output:
- // LastNames := [Yukon Yukon Yukon Doe]
-}
-
-func ExampleDataset_Insert_recordExec() {
- db := getDb()
- insert := db.From("goqu_user").Insert(
- goqu.Record{"first_name": "Jed", "last_name": "Riley", "created": time.Now()},
- )
- if _, err := insert.Exec(); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Println("Inserted 1 user")
- }
-
- users := []goqu.Record{
- {"first_name": "Greg", "last_name": "Farley", "created": time.Now()},
- {"first_name": "Jimmy", "last_name": "Stewart", "created": time.Now()},
- {"first_name": "Jeff", "last_name": "Jeffers", "created": time.Now()},
- }
- if _, err := db.From("goqu_user").Insert(users).Exec(); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Printf("Inserted %d users", len(users))
- }
-
- // Output:
- // Inserted 1 user
- // Inserted 3 users
-}
-
-func ExampleDataset_Insert_recordReturning() {
- db := getDb()
-
- type User struct {
- ID sql.NullInt64 `db:"id"`
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- Created time.Time `db:"created"`
- }
-
- insert := db.From("goqu_user").Returning(goqu.C("id")).Insert(
- goqu.Record{"first_name": "Jed", "last_name": "Riley", "created": time.Now()},
- )
- var id int64
- if _, err := insert.ScanVal(&id); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Printf("Inserted 1 user id:=%d\n", id)
- }
-
- insert = db.From("goqu_user").Returning(goqu.Star()).Insert([]goqu.Record{
- {"first_name": "Greg", "last_name": "Farley", "created": time.Now()},
- {"first_name": "Jimmy", "last_name": "Stewart", "created": time.Now()},
- {"first_name": "Jeff", "last_name": "Jeffers", "created": time.Now()},
- })
- var insertedUsers []User
- if err := insert.ScanStructs(&insertedUsers); err != nil {
- fmt.Println(err.Error())
- } else {
- for _, u := range insertedUsers {
- fmt.Printf("Inserted user: [ID=%d], [FirstName=%+s] [LastName=%s]\n", u.ID.Int64, u.FirstName, u.LastName)
- }
-
- }
-
- // Output:
- // Inserted 1 user id:=5
- // Inserted user: [ID=6], [FirstName=Greg] [LastName=Farley]
- // Inserted user: [ID=7], [FirstName=Jimmy] [LastName=Stewart]
- // Inserted user: [ID=8], [FirstName=Jeff] [LastName=Jeffers]
-}
-
-func ExampleDataset_Insert_scanStructs() {
- db := getDb()
-
- type User struct {
- ID sql.NullInt64 `db:"id" goqu:"skipinsert"`
- FirstName string `db:"first_name"`
- LastName string `db:"last_name"`
- Created time.Time `db:"created"`
- }
-
- insert := db.From("goqu_user").Returning("id").Insert(
- User{FirstName: "Jed", LastName: "Riley"},
- )
- var id int64
- if _, err := insert.ScanVal(&id); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Printf("Inserted 1 user id:=%d\n", id)
- }
-
- insert = db.From("goqu_user").Returning(goqu.Star()).Insert([]User{
- {FirstName: "Greg", LastName: "Farley", Created: time.Now()},
- {FirstName: "Jimmy", LastName: "Stewart", Created: time.Now()},
- {FirstName: "Jeff", LastName: "Jeffers", Created: time.Now()},
- })
- var insertedUsers []User
- if err := insert.ScanStructs(&insertedUsers); err != nil {
- fmt.Println(err.Error())
- } else {
- for _, u := range insertedUsers {
- fmt.Printf("Inserted user: [ID=%d], [FirstName=%+s] [LastName=%s]\n", u.ID.Int64, u.FirstName, u.LastName)
- }
-
- }
-
- // Output:
- // Inserted 1 user id:=5
- // Inserted user: [ID=6], [FirstName=Greg] [LastName=Farley]
- // Inserted user: [ID=7], [FirstName=Jimmy] [LastName=Stewart]
- // Inserted user: [ID=8], [FirstName=Jeff] [LastName=Jeffers]
-}
-
-func ExampleDataset_Update() {
- db := getDb()
- update := db.From("goqu_user").
- Where(goqu.C("first_name").Eq("Bob")).
- Update(goqu.Record{"first_name": "Bobby"})
-
- if r, err := update.Exec(); err != nil {
- fmt.Println(err.Error())
- } else {
- c, _ := r.RowsAffected()
- fmt.Printf("Updated %d users", c)
- }
-
- // Output:
- // Updated 1 users
-}
-
-func ExampleDataset_Update_returning() {
- db := getDb()
- var ids []int64
- update := db.From("goqu_user").
- Where(goqu.Ex{"last_name": "Yukon"}).
- Returning("id").
- Update(goqu.Record{"last_name": "ucon"})
- if err := update.ScanVals(&ids); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Printf("Updated users with ids %+v", ids)
- }
-
- // Output:
- // Updated users with ids [1 2 3]
-}
-func ExampleDataset_Delete() {
- db := getDb()
-
- de := db.From("goqu_user").
- Where(goqu.Ex{"first_name": "Bob"}).
- Delete()
- if r, err := de.Exec(); err != nil {
- fmt.Println(err.Error())
- } else {
- c, _ := r.RowsAffected()
- fmt.Printf("Deleted %d users", c)
- }
-
- // Output:
- // Deleted 1 users
-}
-
-func ExampleDataset_Delete_returning() {
- db := getDb()
-
- de := db.From("goqu_user").
- Where(goqu.C("last_name").Eq("Yukon")).
- Returning(goqu.C("id")).
- Delete()
-
- var ids []int64
- if err := de.ScanVals(&ids); err != nil {
- fmt.Println(err.Error())
- } else {
- fmt.Printf("Deleted users [ids:=%+v]", ids)
- }
-
- // Output:
- // Deleted users [ids:=[1 2 3]]
-}
diff --git a/dataset_query_test.go b/dataset_query_test.go
deleted file mode 100644
index d66f4315..00000000
--- a/dataset_query_test.go
+++ /dev/null
@@ -1,458 +0,0 @@
-package goqu
-
-import (
- "testing"
-
- "github.com/DATA-DOG/go-sqlmock"
- "github.com/doug-martin/goqu/v7/exec"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/suite"
-)
-
-type dsTestActionItem struct {
- Address string `db:"address"`
- Name string `db:"name"`
-}
-
-type datasetQuerySuite struct {
- suite.Suite
-}
-
-func (dqs *datasetQuerySuite) queryFactory(db exec.DbExecutor) exec.QueryFactory {
- return exec.NewQueryFactory(db)
-}
-
-func (dqs *datasetQuerySuite) TestScanStructs() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(`SELECT "address", "name" FROM "items"`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
- FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
-
- mock.ExpectQuery(`SELECT DISTINCT "name" FROM "items"`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
- FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
-
- mock.ExpectQuery(`SELECT "test" FROM "items"`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var items []dsTestActionItem
- assert.NoError(t, ds.From("items").ScanStructs(&items))
- assert.Equal(t, items, []dsTestActionItem{
- {Address: "111 Test Addr", Name: "Test1"},
- {Address: "211 Test Addr", Name: "Test2"},
- })
-
- items = items[0:0]
- assert.NoError(t, ds.From("items").SelectDistinct("name").ScanStructs(&items))
- assert.Equal(t, items, []dsTestActionItem{
- {Address: "111 Test Addr", Name: "Test1"},
- {Address: "211 Test Addr", Name: "Test2"},
- })
-
- items = items[0:0]
- assert.EqualError(t, ds.From("items").ScanStructs(items),
- "goqu: type must be a pointer to a slice when scanning into structs")
- assert.EqualError(t, ds.From("items").ScanStructs(&dsTestActionItem{}),
- "goqu: type must be a pointer to a slice when scanning into structs")
- assert.EqualError(t, ds.From("items").Select("test").ScanStructs(&items),
- `goqu: unable to find corresponding field to column "test" returned by query`)
-}
-
-func (dqs *datasetQuerySuite) TestScanStructs_WithPreparedStatements() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(
- `SELECT "address", "name" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
- WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
- FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
-
- mock.ExpectQuery(
- `SELECT "test" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
- WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var items []dsTestActionItem
- assert.NoError(t, ds.From("items").Prepared(true).Where(Ex{
- "name": []string{"Bob", "Sally", "Billy"},
- "address": "111 Test Addr",
- }).ScanStructs(&items))
- assert.Equal(t, items, []dsTestActionItem{
- {Address: "111 Test Addr", Name: "Test1"},
- {Address: "211 Test Addr", Name: "Test2"},
- })
-
- items = items[0:0]
- assert.EqualError(t, ds.From("items").ScanStructs(items),
- "goqu: type must be a pointer to a slice when scanning into structs")
- assert.EqualError(t, ds.From("items").ScanStructs(&dsTestActionItem{}),
- "goqu: type must be a pointer to a slice when scanning into structs")
- assert.EqualError(t, ds.From("items").
- Prepared(true).
- Select("test").
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- ScanStructs(&items), `goqu: unable to find corresponding field to column "test" returned by query`)
-}
-
-func (dqs *datasetQuerySuite) TestScanStruct() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(`SELECT "address", "name" FROM "items" LIMIT 1`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
-
- mock.ExpectQuery(`SELECT DISTINCT "name" FROM "items" LIMIT 1`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
-
- mock.ExpectQuery(`SELECT "test" FROM "items" LIMIT 1`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var item dsTestActionItem
- found, err := ds.From("items").ScanStruct(&item)
- assert.NoError(t, err)
- assert.True(t, found)
- assert.Equal(t, item.Address, "111 Test Addr")
- assert.Equal(t, item.Name, "Test1")
-
- item = dsTestActionItem{}
- found, err = ds.From("items").SelectDistinct("name").ScanStruct(&item)
- assert.NoError(t, err)
- assert.True(t, found)
- assert.Equal(t, item.Address, "111 Test Addr")
- assert.Equal(t, item.Name, "Test1")
-
- _, err = ds.From("items").ScanStruct(item)
- assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
- _, err = ds.From("items").ScanStruct([]dsTestActionItem{})
- assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
- _, err = ds.From("items").Select("test").ScanStruct(&item)
- assert.EqualError(t, err, `goqu: unable to find corresponding field to column "test" returned by query`)
-}
-
-func (dqs *datasetQuerySuite) TestScanStruct_WithPreparedStatements() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(
- `SELECT "address", "name" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\) LIMIT \?`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
- WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
-
- mock.ExpectQuery(`SELECT "test" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\) LIMIT \?`).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
- WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var item dsTestActionItem
- found, err := ds.From("items").Prepared(true).Where(Ex{
- "name": []string{"Bob", "Sally", "Billy"},
- "address": "111 Test Addr",
- }).ScanStruct(&item)
- assert.NoError(t, err)
- assert.True(t, found)
- assert.Equal(t, item.Address, "111 Test Addr")
- assert.Equal(t, item.Name, "Test1")
-
- _, err = ds.From("items").ScanStruct(item)
- assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
- _, err = ds.From("items").ScanStruct([]dsTestActionItem{})
- assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
- _, err = ds.From("items").
- Prepared(true).
- Select("test").
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- ScanStruct(&item)
- assert.EqualError(t, err, `goqu: unable to find corresponding field to column "test" returned by query`)
-}
-
-func (dqs *datasetQuerySuite) TestScanVals() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(`SELECT "id" FROM "items"`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var ids []uint32
- assert.NoError(t, ds.From("items").Select("id").ScanVals(&ids))
- assert.Equal(t, ids, []uint32{1, 2, 3, 4, 5})
-
- assert.EqualError(t, ds.From("items").ScanVals([]uint32{}),
- "goqu: type must be a pointer to a slice when scanning into vals")
- assert.EqualError(t, ds.From("items").ScanVals(dsTestActionItem{}),
- "goqu: type must be a pointer to a slice when scanning into vals")
-}
-
-func (dqs *datasetQuerySuite) TestScanVals_WithPreparedStatment() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(
- `SELECT "id" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
- WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var ids []uint32
- assert.NoError(t, ds.From("items").
- Prepared(true).
- Select("id").
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- ScanVals(&ids))
- assert.Equal(t, ids, []uint32{1, 2, 3, 4, 5})
-
- assert.EqualError(t, ds.From("items").ScanVals([]uint32{}),
- "goqu: type must be a pointer to a slice when scanning into vals")
- assert.EqualError(t, ds.From("items").ScanVals(dsTestActionItem{}),
- "goqu: type must be a pointer to a slice when scanning into vals")
-}
-
-func (dqs *datasetQuerySuite) TestScanVal() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(`SELECT "id" FROM "items" LIMIT 1`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var id int64
- found, err := ds.From("items").Select("id").ScanVal(&id)
- assert.NoError(t, err)
- assert.Equal(t, id, int64(10))
- assert.True(t, found)
-
- found, err = ds.From("items").ScanVal([]int64{})
- assert.False(t, found)
- assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
- found, err = ds.From("items").ScanVal(10)
- assert.False(t, found)
- assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
-}
-
-func (dqs *datasetQuerySuite) TestScanVal_WithPreparedStatement() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(
- `SELECT "id" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\) LIMIT ?`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
- WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var id int64
- found, err := ds.From("items").
- Prepared(true).
- Select("id").
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- ScanVal(&id)
- assert.NoError(t, err)
- assert.Equal(t, id, int64(10))
- assert.True(t, found)
-
- found, err = ds.From("items").ScanVal([]int64{})
- assert.False(t, found)
- assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
- found, err = ds.From("items").ScanVal(10)
- assert.False(t, found)
- assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
-}
-
-func (dqs *datasetQuerySuite) TestCount() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(`SELECT COUNT\(\*\) AS "count" FROM "items"`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"count"}).FromCSVString("10"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- count, err := ds.From("items").Count()
- assert.NoError(t, err)
- assert.Equal(t, count, int64(10))
-}
-
-func (dqs *datasetQuerySuite) TestCount_WithPreparedStatement() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(
- `SELECT COUNT\(\*\) AS "count" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
- WillReturnRows(sqlmock.NewRows([]string{"count"}).FromCSVString("10"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- count, err := ds.From("items").
- Prepared(true).
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- Count()
- assert.NoError(t, err)
- assert.Equal(t, count, int64(10))
-}
-
-func (dqs *datasetQuerySuite) TestPluck() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(`SELECT "name" FROM "items"`).
- WithArgs().
- WillReturnRows(sqlmock.NewRows([]string{"name"}).FromCSVString("test1\ntest2\ntest3\ntest4\ntest5"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var names []string
- assert.NoError(t, ds.From("items").Pluck(&names, "name"))
- assert.Equal(t, names, []string{"test1", "test2", "test3", "test4", "test5"})
-}
-
-func (dqs *datasetQuerySuite) TestPluck_WithPreparedStatement() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectQuery(
- `SELECT "name" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
- ).
- WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
- WillReturnRows(sqlmock.NewRows([]string{"name"}).FromCSVString("Bob\nSally\nBilly"))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- var names []string
- assert.NoError(t, ds.From("items").
- Prepared(true).
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- Pluck(&names, "name"))
- assert.Equal(t, names, []string{"Bob", "Sally", "Billy"})
-}
-
-func (dqs *datasetQuerySuite) TestUpdate() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectExec(`UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE \("name" IS NULL\)`).
- WithArgs().
- WillReturnResult(sqlmock.NewResult(0, 0))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- _, err = ds.From("items").Where(C("name").IsNull()).Update(Record{
- "address": "111 Test Addr",
- "name": "Test1",
- }).Exec()
- assert.NoError(t, err)
-}
-
-func (dqs *datasetQuerySuite) TestUpdate_WithPreparedStatement() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectExec(
- `UPDATE "items" SET "address"=\?,"name"=\? WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
- ).
- WithArgs("112 Test Addr", "Test1", "111 Test Addr", "Bob", "Sally", "Billy").
- WillReturnResult(sqlmock.NewResult(0, 0))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- _, err = ds.From("items").
- Prepared(true).
- Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
- Update(Record{"address": "112 Test Addr", "name": "Test1"}).
- Exec()
- assert.NoError(t, err)
-}
-
-func (dqs *datasetQuerySuite) TestInsert() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectExec(`INSERT INTO "items" \("address", "name"\) VALUES \('111 Test Addr', 'Test1'\)`).
- WithArgs().
- WillReturnResult(sqlmock.NewResult(0, 0))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- _, err = ds.From("items").Insert(Record{"address": "111 Test Addr", "name": "Test1"}).Exec()
- assert.NoError(t, err)
-}
-
-func (dqs *datasetQuerySuite) TestInsert_WithPreparedStatment() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectExec(`INSERT INTO "items" \("address", "name"\) VALUES \(\?, \?\), \(\?, \?\)`).
- WithArgs("111 Test Addr", "Test1", "112 Test Addr", "Test2").
- WillReturnResult(sqlmock.NewResult(0, 0))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- _, err = ds.From("items").
- Prepared(true).
- Insert(
- Record{"address": "111 Test Addr", "name": "Test1"},
- Record{"address": "112 Test Addr", "name": "Test2"},
- ).
- Exec()
- assert.NoError(t, err)
-}
-
-func (dqs *datasetQuerySuite) TestDelete() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectExec(`DELETE FROM "items" WHERE \("id" > 10\)`).
- WithArgs().
- WillReturnResult(sqlmock.NewResult(0, 0))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- _, err = ds.From("items").Where(C("id").Gt(10)).Delete().Exec()
- assert.NoError(t, err)
-}
-
-func (dqs *datasetQuerySuite) TestDelete_WithPreparedStatment() {
- t := dqs.T()
- mDb, mock, err := sqlmock.New()
- assert.NoError(t, err)
- mock.ExpectExec(`DELETE FROM "items" WHERE \("id" > \?\)`).
- WithArgs(10).
- WillReturnResult(sqlmock.NewResult(0, 0))
-
- qf := dqs.queryFactory(mDb)
- ds := newDataset("mock", qf)
- _, err = ds.From("items").Prepared(true).Where(Ex{"id": Op{"gt": 10}}).Delete().Exec()
- assert.NoError(t, err)
-}
-
-func TestDatasetQuerySuite(t *testing.T) {
- suite.Run(t, new(datasetQuerySuite))
-}
diff --git a/dataset_sql_test.go b/dataset_sql_test.go
deleted file mode 100644
index 47d39c30..00000000
--- a/dataset_sql_test.go
+++ /dev/null
@@ -1,2842 +0,0 @@
-package goqu
-
-import (
- "database/sql"
- "database/sql/driver"
- "fmt"
- "testing"
- "time"
-
- "github.com/doug-martin/goqu/v7/exp"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/suite"
-)
-
-type datasetIntegrationTest struct {
- suite.Suite
-}
-
-func (dit *datasetIntegrationTest) SetupSuite() {
- noReturn := DefaultDialectOptions()
- noReturn.SupportsReturn = false
- RegisterDialect("no-return", noReturn)
-
- limitOnDelete := DefaultDialectOptions()
- limitOnDelete.SupportsLimitOnDelete = true
- RegisterDialect("limit-on-delete", limitOnDelete)
-
- orderOnDelete := DefaultDialectOptions()
- orderOnDelete.SupportsOrderByOnDelete = true
- RegisterDialect("order-on-delete", orderOnDelete)
-
- limitOnUpdate := DefaultDialectOptions()
- limitOnUpdate.SupportsLimitOnUpdate = true
- RegisterDialect("limit-on-update", limitOnUpdate)
-
- orderOnUpdate := DefaultDialectOptions()
- orderOnUpdate.SupportsOrderByOnUpdate = true
- RegisterDialect("order-on-update", orderOnUpdate)
-}
-
-func (dit *datasetIntegrationTest) TearDownSuite() {
- DeregisterDialect("no-return")
- DeregisterDialect("limit-on-delete")
- DeregisterDialect("order-on-delete")
- DeregisterDialect("limit-on-update")
- DeregisterDialect("order-on-update")
-}
-
-func (dit *datasetIntegrationTest) TestToDeleteSQLNoReturning() {
- t := dit.T()
- ds1 := New("no-return", nil).From("items")
- _, _, err := ds1.Returning("id").ToDeleteSQL()
- assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
-}
-
-func (dit *datasetIntegrationTest) TestToDeleteSQLWithLimit() {
- t := dit.T()
- ds1 := New("limit-on-delete", nil).From("items")
- dsql, _, err := ds1.Limit(10).ToDeleteSQL()
- assert.Nil(t, err)
- assert.Equal(t, dsql, `DELETE FROM "items" LIMIT 10`)
-}
-
-func (dit *datasetIntegrationTest) TestToDeleteSQLWithOrder() {
- t := dit.T()
- ds1 := New("order-on-delete", nil).From("items")
- dsql, _, err := ds1.Order(C("name").Desc()).ToDeleteSQL()
- assert.Nil(t, err)
- assert.Equal(t, dsql, `DELETE FROM "items" ORDER BY "name" DESC`)
-}
-
-func (dit *datasetIntegrationTest) TestToDeleteSQLNoSources() {
- t := dit.T()
- ds1 := From("items")
- _, _, err := ds1.From().ToDeleteSQL()
- assert.EqualError(t, err, "goqu: no source found when generating delete sql")
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToDeleteSQL() {
- t := dit.T()
- ds1 := From("items")
- dsql, args, err := ds1.Prepared(true).ToDeleteSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, dsql, `DELETE FROM "items"`)
-
- dsql, args, err = ds1.Where(I("id").Eq(1)).Prepared(true).ToDeleteSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, dsql, `DELETE FROM "items" WHERE ("id" = ?)`)
-
- dsql, args, err = ds1.Returning("id").Where(I("id").Eq(1)).Prepared(true).ToDeleteSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, dsql, `DELETE FROM "items" WHERE ("id" = ?) RETURNING "id"`)
-}
-
-func (dit *datasetIntegrationTest) TestToTruncateSQL() {
- t := dit.T()
- ds1 := From("items")
- tsql, _, err := ds1.ToTruncateSQL()
- assert.NoError(t, err)
- assert.Equal(t, tsql, `TRUNCATE "items"`)
-}
-
-func (dit *datasetIntegrationTest) TestToTruncateSQLNoSources() {
- t := dit.T()
- ds1 := From("items")
- _, _, err := ds1.From().ToTruncateSQL()
- assert.EqualError(t, err, "goqu: no source found when generating truncate sql")
-}
-
-func (dit *datasetIntegrationTest) TestToTruncateSQLWithOpts() {
- t := dit.T()
- ds1 := From("items")
- tsql, _, err := ds1.ToTruncateWithOptsSQL(TruncateOptions{Cascade: true})
- assert.NoError(t, err)
- assert.Equal(t, tsql, `TRUNCATE "items" CASCADE`)
-
- tsql, _, err = ds1.ToTruncateWithOptsSQL(TruncateOptions{Restrict: true})
- assert.NoError(t, err)
- assert.Equal(t, tsql, `TRUNCATE "items" RESTRICT`)
-
- tsql, _, err = ds1.ToTruncateWithOptsSQL(TruncateOptions{Identity: "restart"})
- assert.NoError(t, err)
- assert.Equal(t, tsql, `TRUNCATE "items" RESTART IDENTITY`)
-
- tsql, _, err = ds1.ToTruncateWithOptsSQL(TruncateOptions{Identity: "continue"})
- assert.NoError(t, err)
- assert.Equal(t, tsql, `TRUNCATE "items" CONTINUE IDENTITY`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToTruncateSQL() {
- t := dit.T()
- ds1 := From("items")
- tsql, args, err := ds1.ToTruncateSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, tsql, `TRUNCATE "items"`)
-}
-
-func (dit *datasetIntegrationTest) TestInsertNullTime() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- CreatedAt *time.Time `db:"created_at"`
- }
- insertSQL, _, err := ds1.ToInsertSQL(item{CreatedAt: nil})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("created_at") VALUES (NULL)`)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLNoReturning() {
- t := dit.T()
- ds1 := New("no-return", nil).From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- _, _, err := ds1.Returning("id").ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
-
- _, _, err = ds1.Returning("id").ToInsertSQL(From("test2"))
- assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
-}
-
-func (dit *datasetIntegrationTest) TestInsert_InvalidValue() {
- t := dit.T()
- ds1 := From("no-return").From("items")
- _, _, err := ds1.ToInsertSQL(true)
- assert.EqualError(t, err, "goqu: unsupported insert must be map, goqu.Record, or struct type got: bool")
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithStructs() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- Created time.Time `db:"created"`
- }
- created, _ := time.Parse("2006-01-02", "2015-01-01")
- insertSQL, _, err := ds1.ToInsertSQL(item{Name: "Test", Address: "111 Test Addr", Created: created})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL,
- `INSERT INTO "items" ("address", "created", "name") VALUES ('111 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test')`,
- ) // #nosec
-
- insertSQL, _, err = ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Created: created},
- item{Address: "211 Test Addr", Name: "Test2", Created: created},
- item{Address: "311 Test Addr", Name: "Test3", Created: created},
- item{Address: "411 Test Addr", Name: "Test4", Created: created},
- )
- assert.NoError(t, err)
- assert.Equal(t, insertSQL,
- `INSERT INTO "items" ("address", "created", "name") VALUES `+
- `('111 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test1'), `+
- `('211 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test2'), `+
- `('311 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test3'), `+
- `('411 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test4')`,
- )
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithEmbeddedStruct() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- }
- type item struct {
- Phone
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, _, err := ds1.ToInsertSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Phone: Phone{
- Home: "123123",
- Primary: "456456",
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
- `('111 Test Addr', '123123', 'Test', '456456')`)
-
- insertSQL, _, err = ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Phone: Phone{Home: "123123", Primary: "456456"}},
- item{Address: "211 Test Addr", Name: "Test2", Phone: Phone{Home: "123123", Primary: "456456"}},
- item{Address: "311 Test Addr", Name: "Test3", Phone: Phone{Home: "123123", Primary: "456456"}},
- item{Address: "411 Test Addr", Name: "Test4", Phone: Phone{Home: "123123", Primary: "456456"}},
- )
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
- `('111 Test Addr', '123123', 'Test1', '456456'), `+
- `('211 Test Addr', '123123', 'Test2', '456456'), `+
- `('311 Test Addr', '123123', 'Test3', '456456'), `+
- `('411 Test Addr', '123123', 'Test4', '456456')`)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithEmbeddedStructPtr() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- }
- type item struct {
- *Phone
- Address string `db:"address"`
- Name string `db:"name"`
- Valuer sql.NullInt64 `db:"valuer"`
- }
- insertSQL, _, err := ds1.ToInsertSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Valuer: sql.NullInt64{Int64: 10, Valid: true},
- Phone: &Phone{Home: "123123", Primary: "456456"},
- })
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" `+
- `("address", "home_phone", "name", "primary_phone", "valuer")`+
- ` VALUES ('111 Test Addr', '123123', 'Test', '456456', 10)`)
-
- insertSQL, _, err = ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Phone: &Phone{Home: "123123", Primary: "456456"}},
- item{Address: "211 Test Addr", Name: "Test2", Phone: &Phone{Home: "123123", Primary: "456456"}},
- item{Address: "311 Test Addr", Name: "Test3", Phone: &Phone{Home: "123123", Primary: "456456"}},
- item{Address: "411 Test Addr", Name: "Test4", Phone: &Phone{Home: "123123", Primary: "456456"}},
- )
- assert.NoError(t, err)
- assert.Equal(t, insertSQL,
- `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone", "valuer") VALUES `+
- `('111 Test Addr', '123123', 'Test1', '456456', NULL), `+
- `('211 Test Addr', '123123', 'Test2', '456456', NULL), `+
- `('311 Test Addr', '123123', 'Test3', '456456', NULL), `+
- `('411 Test Addr', '123123', 'Test4', '456456', NULL)`)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithValuer() {
- t := dit.T()
- ds1 := From("items")
-
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- Valuer sql.NullInt64 `db:"valuer"`
- }
- sqlString, _, err := ds1.ToInsertSQL(item{Name: "Test", Address: "111 Test Addr", Valuer: sql.NullInt64{Int64: 10, Valid: true}})
- assert.NoError(t, err)
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES ('111 Test Addr', 'Test', 10)`)
-
- sqlString, _, err = ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Valuer: sql.NullInt64{Int64: 10, Valid: true}},
- item{Address: "211 Test Addr", Name: "Test2", Valuer: sql.NullInt64{Int64: 10, Valid: true}},
- item{Address: "311 Test Addr", Name: "Test3", Valuer: sql.NullInt64{Int64: 10, Valid: true}},
- item{Address: "411 Test Addr", Name: "Test4", Valuer: sql.NullInt64{Int64: 10, Valid: true}},
- )
- assert.NoError(t, err)
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
- `('111 Test Addr', 'Test1', 10), `+
- `('211 Test Addr', 'Test2', 10), `+
- `('311 Test Addr', 'Test3', 10), `+
- `('411 Test Addr', 'Test4', 10)`)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithValuerNull() {
- t := dit.T()
- ds1 := From("items")
-
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- Valuer sql.NullInt64 `db:"valuer"`
- }
- sqlString, _, err := ds1.ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(
- t,
- sqlString,
- `INSERT INTO "items" ("address", "name", "valuer") VALUES ('111 Test Addr', 'Test', NULL)`,
- )
-
- sqlString, _, err = ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1"},
- item{Address: "211 Test Addr", Name: "Test2"},
- item{Address: "311 Test Addr", Name: "Test3"},
- item{Address: "411 Test Addr", Name: "Test4"},
- )
- assert.NoError(t, err)
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
- `('111 Test Addr', 'Test1', NULL), `+
- `('211 Test Addr', 'Test2', NULL), `+
- `('311 Test Addr', 'Test3', NULL), `+
- `('411 Test Addr', 'Test4', NULL)`)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithMaps() {
- t := dit.T()
- ds1 := From("items")
-
- insertSQL, _, err := ds1.ToInsertSQL(map[string]interface{}{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`)
-
- insertSQL, _, err = ds1.ToInsertSQL(
- map[string]interface{}{"address": "111 Test Addr", "name": "Test1"},
- map[string]interface{}{"address": "211 Test Addr", "name": "Test2"},
- map[string]interface{}{"address": "311 Test Addr", "name": "Test3"},
- map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
- )
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES `+
- `('111 Test Addr', 'Test1'), `+
- `('211 Test Addr', 'Test2'), `+
- `('311 Test Addr', 'Test3'), `+
- `('411 Test Addr', 'Test4')`)
-
- _, _, err = ds1.ToInsertSQL(
- map[string]interface{}{"address": "111 Test Addr", "name": "Test1"},
- map[string]interface{}{"address": "211 Test Addr"},
- map[string]interface{}{"address": "311 Test Addr", "name": "Test3"},
- map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
- )
- assert.EqualError(t, err, "goqu: rows with different value length expected 2 got 1")
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWitSQLBuilder() {
- t := dit.T()
- ds1 := From("items")
-
- insertSQL, _, err := ds1.ToInsertSQL(From("other_items"))
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" SELECT * FROM "other_items"`)
-}
-
-func (dit *datasetIntegrationTest) TestInsertReturning() {
- t := dit.T()
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- ds1 := From("items").Returning("id")
-
- insertSQL, _, err := ds1.Returning("id").ToInsertSQL(From("other_items"))
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" SELECT * FROM "other_items" RETURNING "id"`)
-
- insertSQL, _, err = ds1.ToInsertSQL(map[string]interface{}{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(
- t,
- insertSQL,
- `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test') RETURNING "id"`,
- )
-
- insertSQL, _, err = ds1.ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(
- t,
- insertSQL,
- `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test') RETURNING "id"`,
- )
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithNoFrom() {
- t := dit.T()
- ds1 := From("test").From()
- _, _, err := ds1.ToInsertSQL(map[string]interface{}{"address": "111 Test Addr", "name": "Test1"})
- assert.EqualError(t, err, "goqu: no source found when generating insert sql")
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWithMapsWithDifferentLengths() {
- t := dit.T()
- ds1 := From("items")
- _, _, err := ds1.ToInsertSQL(
- map[string]interface{}{"address": "111 Test Addr", "name": "Test1"},
- map[string]interface{}{"address": "211 Test Addr"},
- map[string]interface{}{"address": "311 Test Addr", "name": "Test3"},
- map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
- )
- assert.EqualError(t, err, "goqu: rows with different value length expected 2 got 1")
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLWitDifferentKeys() {
- t := dit.T()
- ds1 := From("items")
- _, _, err := ds1.ToInsertSQL(
- map[string]interface{}{"address": "111 Test Addr", "name": "test"},
- map[string]interface{}{"phoneNumber": 10, "address": "111 Test Addr"},
- )
- assert.EqualError(
- t,
- err,
- `goqu: rows with different keys expected ["address","name"] got ["address","phoneNumber"]`,
- )
-}
-
-func (dit *datasetIntegrationTest) TestToInsertSQLDifferentTypes() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- type item2 struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- _, _, err := ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1"},
- item2{Address: "211 Test Addr", Name: "Test2"},
- item{Address: "311 Test Addr", Name: "Test3"},
- item2{Address: "411 Test Addr", Name: "Test4"},
- )
- assert.EqualError(t, err, "goqu: rows must be all the same type expected goqu.item got goqu.item2")
-
- _, _, err = ds1.ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1"},
- map[string]interface{}{"address": "211 Test Addr", "name": "Test2"},
- item{Address: "311 Test Addr", Name: "Test3"},
- map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
- )
- assert.EqualError(
- t,
- err,
- "goqu: rows must be all the same type expected goqu.item got map[string]interface {}",
- )
-}
-
-func (dit *datasetIntegrationTest) TestInsertWithGoquPkTagSQL() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- ID uint32 `db:"id" goqu:"pk,skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, _, err := ds1.ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`)
-
- insertSQL, _, err = ds1.ToInsertSQL(map[string]interface{}{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`)
-
- insertSQL, _, err = ds1.ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "211 Test Addr"},
- item{Name: "Test3", Address: "311 Test Addr"},
- item{Name: "Test4", Address: "411 Test Addr"},
- )
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES `+
- `('111 Test Addr', 'Test1'), `+
- `('211 Test Addr', 'Test2'), `+
- `('311 Test Addr', 'Test3'), `+
- `('411 Test Addr', 'Test4')`)
-}
-
-func (dit *datasetIntegrationTest) TestInsertWithGoquSkipInsertTagSQL() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- ID uint32 `db:"id" goqu:"skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, _, err := ds1.ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`)
-
- insertSQL, _, err = ds1.ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "211 Test Addr"},
- item{Name: "Test3", Address: "311 Test Addr"},
- item{Name: "Test4", Address: "411 Test Addr"},
- )
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES `+
- `('111 Test Addr', 'Test1'), `+
- `('211 Test Addr', 'Test2'), `+
- `('311 Test Addr', 'Test3'), `+
- `('411 Test Addr', 'Test4')`)
-}
-
-func (dit *datasetIntegrationTest) TestInsertDefaultValues() {
- t := dit.T()
- ds1 := From("items")
-
- insertSQL, _, err := ds1.ToInsertSQL()
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" DEFAULT VALUES`)
-
- insertSQL, _, err = ds1.ToInsertSQL(map[string]interface{}{"name": Default(), "address": Default()})
- assert.NoError(t, err)
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (DEFAULT, DEFAULT)`)
-
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWithStructs() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1"},
- item{Address: "211 Test Addr", Name: "Test2"},
- item{Address: "311 Test Addr", Name: "Test3"},
- item{Address: "411 Test Addr", Name: "Test4"},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr",
- "Test1",
- "211 Test Addr",
- "Test2",
- "311 Test Addr",
- "Test3",
- "411 Test Addr",
- "Test4",
- })
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?), (?, ?), (?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWithMaps() {
- t := dit.T()
- ds1 := From("items")
-
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL(map[string]interface{}{
- "name": "Test",
- "address": "111 Test Addr",
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(
- map[string]interface{}{"address": "111 Test Addr", "name": "Test1"},
- map[string]interface{}{"address": "211 Test Addr", "name": "Test2"},
- map[string]interface{}{"address": "311 Test Addr", "name": "Test3"},
- map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr",
- "Test1",
- "211 Test Addr",
- "Test2",
- "311 Test Addr",
- "Test3",
- "411 Test Addr",
- "Test4",
- })
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?), (?, ?), (?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWitSQLBuilder() {
- t := dit.T()
- ds1 := From("items")
-
- insertSQL, args, err := ds1.
- Prepared(true).
- ToInsertSQL(
- From("other_items").Where(C("b").Gt(10)),
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(10)})
- assert.Equal(t, insertSQL, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedInsertReturning() {
- t := dit.T()
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- ds1 := From("items").Returning("id")
-
- insertSQL, args, err := ds1.
- Returning("id").
- Prepared(true).
- ToInsertSQL(From("other_items").Where(C("b").Gt(10)))
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(10)})
- assert.Equal(t, insertSQL, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > ?) RETURNING "id"`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(map[string]interface{}{
- "name": "Test",
- "address": "111 Test Addr",
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?) RETURNING "id"`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?) RETURNING "id"`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedInsertWithGoquPkTagSQL() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- ID uint32 `db:"id" goqu:"pk,skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(map[string]interface{}{
- "name": "Test",
- "address": "111 Test Addr",
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "211 Test Addr"},
- item{Name: "Test3", Address: "311 Test Addr"},
- item{Name: "Test4", Address: "411 Test Addr"},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr",
- "Test1",
- "211 Test Addr",
- "Test2",
- "311 Test Addr",
- "Test3",
- "411 Test Addr",
- "Test4",
- })
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?), (?, ?), (?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedInsertWithGoquSkipInsertTagSQL() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- ID uint32 `db:"id" goqu:"skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "211 Test Addr"},
- item{Name: "Test3", Address: "311 Test Addr"},
- item{Name: "Test4", Address: "411 Test Addr"},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr",
- "Test1",
- "211 Test Addr",
- "Test2",
- "311 Test Addr",
- "Test3",
- "411 Test Addr",
- "Test4",
- })
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?), (?, ?), (?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedInsertDefaultValues() {
- t := dit.T()
- ds1 := From("items")
-
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, insertSQL, `INSERT INTO "items" DEFAULT VALUES`)
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(map[string]interface{}{
- "name": Default(),
- "address": Default(),
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "name") VALUES (DEFAULT, DEFAULT)`)
-
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWithEmbeddedStruct() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- }
- type item struct {
- Phone
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Phone: Phone{
- Home: "123123",
- Primary: "456456",
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "123123", "Test", "456456"})
- assert.Equal(
- t,
- insertSQL,
- `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES (?, ?, ?, ?)`,
- )
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Phone: Phone{Home: "123123", Primary: "456456"}},
- item{Address: "211 Test Addr", Name: "Test2", Phone: Phone{Home: "123123", Primary: "456456"}},
- item{Address: "311 Test Addr", Name: "Test3", Phone: Phone{Home: "123123", Primary: "456456"}},
- item{Address: "411 Test Addr", Name: "Test4", Phone: Phone{Home: "123123", Primary: "456456"}},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr", "123123", "Test1", "456456",
- "211 Test Addr", "123123", "Test2", "456456",
- "311 Test Addr", "123123", "Test3", "456456",
- "411 Test Addr", "123123", "Test4", "456456",
- })
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
- `(?, ?, ?, ?), `+
- `(?, ?, ?, ?), `+
- `(?, ?, ?, ?), `+
- `(?, ?, ?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWithEmbeddedStructPtr() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- }
- type item struct {
- *Phone
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, args, err := ds1.Prepared(true).ToInsertSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Phone: &Phone{
- Home: "123123",
- Primary: "456456",
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "123123", "Test", "456456"})
- assert.Equal(
- t,
- insertSQL,
- `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES (?, ?, ?, ?)`,
- )
-
- insertSQL, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Phone: &Phone{Home: "123123", Primary: "456456"}},
- item{Address: "211 Test Addr", Name: "Test2", Phone: &Phone{Home: "123123", Primary: "456456"}},
- item{Address: "311 Test Addr", Name: "Test3", Phone: &Phone{Home: "123123", Primary: "456456"}},
- item{Address: "411 Test Addr", Name: "Test4", Phone: &Phone{Home: "123123", Primary: "456456"}},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr", "123123", "Test1", "456456",
- "211 Test Addr", "123123", "Test2", "456456",
- "311 Test Addr", "123123", "Test3", "456456",
- "411 Test Addr", "123123", "Test4", "456456",
- })
- assert.Equal(t, insertSQL, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
- `(?, ?, ?, ?), `+
- `(?, ?, ?, ?), `+
- `(?, ?, ?, ?), `+
- `(?, ?, ?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWithValuer() {
- t := dit.T()
- ds1 := From("items")
-
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- Valuer sql.NullInt64 `db:"valuer"`
- }
- sqlString, args, err := ds1.Prepared(true).ToInsertSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Valuer: sql.NullInt64{Int64: 10, Valid: true},
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr", "Test", int64(10),
- })
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES (?, ?, ?)`)
-
- sqlString, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1", Valuer: sql.NullInt64{Int64: 10, Valid: true}},
- item{Address: "211 Test Addr", Name: "Test2", Valuer: sql.NullInt64{Int64: 20, Valid: true}},
- item{Address: "311 Test Addr", Name: "Test3", Valuer: sql.NullInt64{Int64: 30, Valid: true}},
- item{Address: "411 Test Addr", Name: "Test4", Valuer: sql.NullInt64{Int64: 40, Valid: true}},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr", "Test1", int64(10),
- "211 Test Addr", "Test2", int64(20),
- "311 Test Addr", "Test3", int64(30),
- "411 Test Addr", "Test4", int64(40),
- })
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
- `(?, ?, ?), `+
- `(?, ?, ?), `+
- `(?, ?, ?), `+
- `(?, ?, ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToInsertSQLWithValuerNull() {
- t := dit.T()
- ds1 := From("items")
-
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- Valuer sql.NullInt64 `db:"valuer"`
- }
- sqlString, args, err := ds1.Prepared(true).ToInsertSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr", "Test",
- })
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES (?, ?, NULL)`)
-
- sqlString, args, err = ds1.Prepared(true).ToInsertSQL(
- item{Address: "111 Test Addr", Name: "Test1"},
- item{Address: "211 Test Addr", Name: "Test2"},
- item{Address: "311 Test Addr", Name: "Test3"},
- item{Address: "411 Test Addr", Name: "Test4"},
- )
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{
- "111 Test Addr", "Test1",
- "211 Test Addr", "Test2",
- "311 Test Addr", "Test3",
- "411 Test Addr", "Test4",
- })
- assert.Equal(t, sqlString, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
- `(?, ?, NULL), `+
- `(?, ?, NULL), `+
- `(?, ?, NULL), `+
- `(?, ?, NULL)`)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertConflictSQL__OnConflictIsNil() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, _, err := ds1.ToInsertConflictSQL(nil, item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`, insertSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertConflictSQL__OnConflictexpDoUpdate() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- i := item{Name: "Test", Address: "111 Test Addr"}
- insertSQL, _, err := ds1.ToInsertConflictSQL(
- DoUpdate("name", Record{"address": L("excluded.address")}),
- i,
- )
- assert.NoError(t, err)
- assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
- `('111 Test Addr', 'Test') `+
- `ON CONFLICT (name) `+
- `DO UPDATE `+
- `SET "address"=excluded.address`, insertSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertConflictSQL__OnConflictDoUpdateWhere() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- i := item{Name: "Test", Address: "111 Test Addr"}
-
- insertSQL, _, err := ds1.ToInsertConflictSQL(
- DoUpdate("name", Record{"address": L("excluded.address")}).
- Where(C("name").Eq("Test")),
- i,
- )
- assert.NoError(t, err)
- assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
- `('111 Test Addr', 'Test') `+
- `ON CONFLICT (name) `+
- `DO UPDATE `+
- `SET "address"=excluded.address WHERE ("name" = 'Test')`, insertSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToInsertConflictSQLWithDataset__OnConflictDoUpdateWhere() {
- t := dit.T()
- ds1 := From("items")
- ds2 := From("ds2")
-
- insertSQL, _, err := ds1.ToInsertConflictSQL(
- DoUpdate("name", Record{"address": L("excluded.address")}).
- Where(C("name").Eq("Test")),
- ds2,
- )
- assert.NoError(t, err)
- assert.Equal(t, `INSERT INTO "items" `+
- `SELECT * FROM "ds2" `+
- `ON CONFLICT (name) `+
- `DO UPDATE `+
- `SET "address"=excluded.address WHERE ("name" = 'Test')`, insertSQL)
-}
-
-func (dit *datasetIntegrationTest) TestInsertConflict__ImplementsConflictExpressionInterface() {
- t := dit.T()
- assert.Implements(t, (*exp.ConflictExpression)(nil), DoNothing())
- assert.Implements(t, (*exp.ConflictExpression)(nil), DoUpdate("", nil))
-}
-
-func (dit *datasetIntegrationTest) TestToInsertIgnoreSQL() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- insertSQL, _, err := ds1.ToInsertIgnoreSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
- `('111 Test Addr', 'Test') `+
- `ON CONFLICT DO NOTHING`, insertSQL)
-}
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithNoSources() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- _, _, err := ds1.From().ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.EqualError(t, err, "goqu: no source found when generating update sql")
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLNoReturning() {
- t := dit.T()
- ds1 := New("no-return", nil).From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- _, _, err := ds1.Returning("id").ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithLimit() {
- t := dit.T()
- ds1 := New("limit-on-update", nil).From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, _, err := ds1.Limit(10).ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.Nil(t, err)
- assert.Equal(t, updateSQL, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' LIMIT 10`)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithOrder() {
- t := dit.T()
- ds1 := New("order-on-update", nil).From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, _, err := ds1.Order(C("name").Desc()).ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.Nil(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' ORDER BY "name" DESC`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithStructs() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, _, err := ds1.ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test'`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithMaps() {
- t := dit.T()
- ds1 := From("items")
- updateSQL, _, err := ds1.ToUpdateSQL(Record{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test'`, updateSQL)
-
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithByteSlice() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data []byte `db:"data"`
- }
- updateSQL, _, err := ds1.
- Returning(T("items").All()).
- ToUpdateSQL(item{Name: "Test", Data: []byte(`{"someJson":"data"}`)})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"='{"someJson":"data"}',"name"='Test' RETURNING "items".*`, updateSQL)
-}
-
-type valuerType []byte
-
-func (j valuerType) Value() (driver.Value, error) {
- return []byte(fmt.Sprintf("%s World", string(j))), nil
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithCustomValuer() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data valuerType `db:"data"`
- }
- updateSQL, _, err := ds1.
- Returning(T("items").All()).
- ToUpdateSQL(item{Name: "Test", Data: []byte(`Hello`)})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"='Hello World',"name"='Test' RETURNING "items".*`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithValuer() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data sql.NullString `db:"data"`
- }
-
- updateSQL, _, err := ds1.
- Returning(T("items").All()).
- ToUpdateSQL(item{Name: "Test", Data: sql.NullString{String: "Hello World", Valid: true}})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"='Hello World',"name"='Test' RETURNING "items".*`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithValuerNull() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data sql.NullString `db:"data"`
- }
- updateSQL, _, err := ds1.Returning(T("items").All()).ToUpdateSQL(item{Name: "Test"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"=NULL,"name"='Test' RETURNING "items".*`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithEmbeddedStruct() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- Created time.Time `db:"phone_created"`
- }
- type item struct {
- Phone
- Address string `db:"address" goqu:"skipupdate"`
- Name string `db:"name"`
- Created time.Time `db:"created"`
- NilPointer interface{} `db:"nil_pointer"`
- }
- created, _ := time.Parse("2006-01-02", "2015-01-01")
-
- updateSQL, args, err := ds1.ToUpdateSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Created: created,
- Phone: Phone{
- Home: "123123",
- Primary: "456456",
- Created: created,
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, `UPDATE "items" SET `+
- `"created"='2015-01-01T00:00:00Z',`+
- `"home_phone"='123123',`+
- `"name"='Test',`+
- `"nil_pointer"=NULL,`+
- `"phone_created"='2015-01-01T00:00:00Z',`+
- `"primary_phone"='456456'`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithEmbeddedStructPtr() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- Created time.Time `db:"phone_created"`
- }
- type item struct {
- *Phone
- Address string `db:"address" goqu:"skipupdate"`
- Name string `db:"name"`
- Created time.Time `db:"created"`
- }
- created, _ := time.Parse("2006-01-02", "2015-01-01")
-
- updateSQL, args, err := ds1.ToUpdateSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Created: created,
- Phone: &Phone{
- Home: "123123",
- Primary: "456456",
- Created: created,
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, `UPDATE "items" SET `+
- `"created"='2015-01-01T00:00:00Z',`+
- `"home_phone"='123123',`+
- `"name"='Test',`+
- `"phone_created"='2015-01-01T00:00:00Z',`+
- `"primary_phone"='456456'`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithUnsupportedType() {
- t := dit.T()
- ds1 := From("items")
- _, _, err := ds1.ToUpdateSQL([]string{"HELLO"})
- assert.EqualError(t, err, "goqu: unsupported update interface type []string")
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithSkipupdateTag() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address" goqu:"skipupdate"`
- Name string `db:"name"`
- }
- updateSQL, _, err := ds1.ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "name"='Test'`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithWhere() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, _, err := ds1.
- Where(C("name").IsNull()).
- ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' WHERE ("name" IS NULL)`, updateSQL)
-
- updateSQL, _, err = ds1.
- Where(C("name").IsNull()).
- ToUpdateSQL(Record{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' WHERE ("name" IS NULL)`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestToUpdateSQLWithReturning() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, _, err := ds1.
- Returning(T("items").All()).
- ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' RETURNING "items".*`, updateSQL)
-
- updateSQL, _, err = ds1.
- Where(C("name").IsNull()).
- Returning(L(`"items".*`)).
- ToUpdateSQL(Record{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' WHERE ("name" IS NULL) RETURNING "items".*`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithStructs() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, args, err := ds1.
- Prepared(true).
- ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=?`, updateSQL)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithMaps() {
- t := dit.T()
- ds1 := From("items")
- updateSQL, args, err := ds1.
- Prepared(true).
- ToUpdateSQL(Record{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
- assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=?`, updateSQL)
-
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithByteSlice() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data []byte `db:"data"`
- }
- updateSQL, args, err := ds1.
- Returning(T("items").All()).
- Prepared(true).
- ToUpdateSQL(item{Name: "Test", Data: []byte(`{"someJson":"data"}`)})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"=?,"name"=? RETURNING "items".*`, updateSQL)
- assert.Equal(t, args, []interface{}{[]byte(`{"someJson":"data"}`), "Test"})
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithCustomValuer() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data valuerType `db:"data"`
- }
- updateSQL, args, err := ds1.
- Returning(T("items").All()).
- Prepared(true).
- ToUpdateSQL(item{Name: "Test", Data: []byte(`Hello`)})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"=?,"name"=? RETURNING "items".*`, updateSQL)
- assert.Equal(t, args, []interface{}{[]byte("Hello World"), "Test"})
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithValuer() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Name string `db:"name"`
- Data sql.NullString `db:"data"`
- }
- updateSQL, args, err := ds1.
- Returning(T("items").All()).
- Prepared(true).
- ToUpdateSQL(item{Name: "Test", Data: sql.NullString{String: "Hello World", Valid: true}})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "data"=?,"name"=? RETURNING "items".*`, updateSQL)
- assert.Equal(t, args, []interface{}{"Hello World", "Test"})
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithSkipupdateTag() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address" goqu:"skipupdate"`
- Name string `db:"name"`
- }
- updateSQL, args, err := ds1.Prepared(true).ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "name"=?`, updateSQL)
- assert.Equal(t, args, []interface{}{"Test"})
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithEmbeddedStruct() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- Created time.Time `db:"phone_created"`
- }
- type item struct {
- Phone
- Address string `db:"address" goqu:"skipupdate"`
- Name string `db:"name"`
- Created time.Time `db:"created"`
- NilPointer interface{} `db:"nil_pointer"`
- }
- created, _ := time.Parse("2006-01-02", "2015-01-01")
-
- updateSQL, args, err := ds1.Prepared(true).ToUpdateSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Created: created,
- Phone: Phone{
- Home: "123123",
- Primary: "456456",
- Created: created,
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" `+
- `SET "created"=?,"home_phone"=?,"name"=?,"nil_pointer"=NULL,"phone_created"=?,"primary_phone"=?`, updateSQL)
- assert.Equal(t, []interface{}{created, "123123", "Test", created, "456456"}, args)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithEmbeddedStructPtr() {
- t := dit.T()
- ds1 := From("items")
- type Phone struct {
- Primary string `db:"primary_phone"`
- Home string `db:"home_phone"`
- Created time.Time `db:"phone_created"`
- }
- type item struct {
- *Phone
- Address string `db:"address" goqu:"skipupdate"`
- Name string `db:"name"`
- Created time.Time `db:"created"`
- }
- created, _ := time.Parse("2006-01-02", "2015-01-01")
-
- updateSQL, args, err := ds1.Prepared(true).ToUpdateSQL(item{
- Name: "Test",
- Address: "111 Test Addr",
- Created: created,
- Phone: &Phone{
- Home: "123123",
- Primary: "456456",
- Created: created,
- },
- })
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" `+
- `SET "created"=?,"home_phone"=?,"name"=?,"phone_created"=?,"primary_phone"=?`, updateSQL)
- assert.Equal(t, []interface{}{created, "123123", "Test", created, "456456"}, args)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithWhere() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, args, err := ds1.
- Where(C("name").IsNull()).
- Prepared(true).
- ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
-
- updateSQL, args, err = ds1.
- Where(C("name").IsNull()).
- Prepared(true).
- ToUpdateSQL(Record{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
-}
-
-func (dit *datasetIntegrationTest) TestPreparedToUpdateSQLWithReturning() {
- t := dit.T()
- ds1 := From("items")
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
- updateSQL, args, err := ds1.
- Returning(T("items").All()).
- Prepared(true).
- ToUpdateSQL(item{Name: "Test", Address: "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? RETURNING "items".*`, updateSQL)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
-
- updateSQL, args, err = ds1.
- Where(C("name").IsNull()).
- Returning(L(`"items".*`)).
- Prepared(true).
- ToUpdateSQL(Record{"name": "Test", "address": "111 Test Addr"})
- assert.NoError(t, err)
- assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL) RETURNING "items".*`, updateSQL)
- assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
-}
-
-func (dit *datasetIntegrationTest) TestSelect() {
- t := dit.T()
- ds1 := From("test")
-
- selectSQL, _, err := ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-
- selectSQL, _, err = ds1.Select().ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-
- selectSQL, _, err = ds1.Select("id").ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id" FROM "test"`)
-
- selectSQL, _, err = ds1.Select("id", "name").ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "name" FROM "test"`)
-
- selectSQL, _, err = ds1.Select(L("COUNT(*)").As("count")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT COUNT(*) AS "count" FROM "test"`)
-
- selectSQL, _, err = ds1.Select(C("id").As("other_id"), L("COUNT(*)").As("count")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id" AS "other_id", COUNT(*) AS "count" FROM "test"`)
-
- selectSQL, _, err = ds1.From().Select(ds1.From("test_1").Select("id")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT (SELECT "id" FROM "test_1")`)
-
- selectSQL, _, err = ds1.From().Select(ds1.From("test_1").Select("id").As("test_id")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT (SELECT "id" FROM "test_1") AS "test_id"`)
-
- selectSQL, _, err = ds1.From().
- Select(
- DISTINCT("a").As("distinct"),
- COUNT("a").As("count"),
- L("CASE WHEN ? THEN ? ELSE ? END", MIN("a").Eq(10), true, false),
- L("CASE WHEN ? THEN ? ELSE ? END", AVG("a").Neq(10), true, false),
- L("CASE WHEN ? THEN ? ELSE ? END", FIRST("a").Gt(10), true, false),
- L("CASE WHEN ? THEN ? ELSE ? END", FIRST("a").Gte(10), true, false),
- L("CASE WHEN ? THEN ? ELSE ? END", LAST("a").Lt(10), true, false),
- L("CASE WHEN ? THEN ? ELSE ? END", LAST("a").Lte(10), true, false),
- SUM("a").As("sum"),
- COALESCE(C("a"), "a").As("colaseced"),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT `+
- `DISTINCT("a") AS "distinct", `+
- `COUNT("a") AS "count", `+
- `CASE WHEN (MIN("a") = 10) THEN TRUE ELSE FALSE END, `+
- `CASE WHEN (AVG("a") != 10) THEN TRUE ELSE FALSE END, `+
- `CASE WHEN (FIRST("a") > 10) THEN TRUE ELSE FALSE END, `+
- `CASE WHEN (FIRST("a") >= 10) THEN TRUE ELSE FALSE END,`+
- ` CASE WHEN (LAST("a") < 10) THEN TRUE ELSE FALSE END, `+
- `CASE WHEN (LAST("a") <= 10) THEN TRUE ELSE FALSE END, `+
- `SUM("a") AS "sum", `+
- `COALESCE("a", 'a') AS "colaseced"`)
-
- type MyStruct struct {
- Name string
- Address string `db:"address"`
- EmailAddress string `db:"email_address"`
- FakeCol string `db:"-"`
- }
- selectSQL, _, err = ds1.Select(&MyStruct{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
-
- selectSQL, _, err = ds1.Select(MyStruct{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
-
- type myStruct2 struct {
- MyStruct
- Zipcode string `db:"zipcode"`
- }
-
- selectSQL, _, err = ds1.Select(&myStruct2{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name", "zipcode" FROM "test"`)
-
- selectSQL, _, err = ds1.Select(myStruct2{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name", "zipcode" FROM "test"`)
-
- var myStructs []MyStruct
- selectSQL, _, err = ds1.Select(&myStructs).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
-
- selectSQL, _, err = ds1.Select(myStructs).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
- // should not change original
- selectSQL, _, err = ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestSelectDistinct() {
- t := dit.T()
- ds1 := From("test")
-
- selectSQL, _, err := ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct("id").ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "id" FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct("id", "name").ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "id", "name" FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct(L("COUNT(*)").As("count")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT COUNT(*) AS "count" FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct(C("id").As("other_id"), L("COUNT(*)").As("count")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "id" AS "other_id", COUNT(*) AS "count" FROM "test"`)
-
- type MyStruct struct {
- Name string
- Address string `db:"address"`
- EmailAddress string `db:"email_address"`
- FakeCol string `db:"-"`
- }
- selectSQL, _, err = ds1.SelectDistinct(&MyStruct{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct(MyStruct{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
-
- type myStruct2 struct {
- MyStruct
- Zipcode string `db:"zipcode"`
- }
-
- selectSQL, _, err = ds1.SelectDistinct(&myStruct2{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name", "zipcode" FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct(myStruct2{}).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name", "zipcode" FROM "test"`)
-
- var myStructs []MyStruct
- selectSQL, _, err = ds1.SelectDistinct(&myStructs).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
-
- selectSQL, _, err = ds1.SelectDistinct(myStructs).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
- // should not change original
- selectSQL, _, err = ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
- // should not change original
- selectSQL, _, err = ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestClearSelect() {
- t := dit.T()
- ds1 := From("test")
-
- selectSQL, _, err := ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-
- b := ds1.Select("a").ClearSelect()
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestSelectAppend() {
- t := dit.T()
- ds1 := From("test")
-
- selectSQL, _, err := ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-
- b := ds1.Select("a").SelectAppend("b").SelectAppend("c")
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "a", "b", "c" FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestFrom() {
- t := dit.T()
- ds1 := From("test")
-
- selectSQL, _, err := ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-
- ds2 := ds1.From("test2")
- selectSQL, _, err = ds2.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test2"`)
-
- ds2 = ds1.From("test2", "test3")
- selectSQL, _, err = ds2.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test2", "test3"`)
-
- ds2 = ds1.From(T("test2").As("test_2"), "test3")
- selectSQL, _, err = ds2.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test2" AS "test_2", "test3"`)
-
- ds2 = ds1.From(ds1.From("test2"), "test3")
- selectSQL, _, err = ds2.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (SELECT * FROM "test2") AS "t1", "test3"`)
-
- ds2 = ds1.From(ds1.From("test2").As("test_2"), "test3")
- selectSQL, _, err = ds2.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (SELECT * FROM "test2") AS "test_2", "test3"`)
- // should not change original
- selectSQL, _, err = ds1.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestEmptyWhere() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where()
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestWhere() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Eq(true),
- C("a").Neq(true),
- C("a").Eq(false),
- C("a").Neq(false),
- )
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
- `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
-
- b = ds1.Where(
- C("a").Eq("a"),
- C("b").Neq("b"),
- C("c").Gt("c"),
- C("d").Gte("d"),
- C("e").Lt("e"),
- C("f").Lte("f"),
- )
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
- `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
-
- b = ds1.Where(
- C("a").Eq(From("test2").Select("id")),
- )
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
-
- b = ds1.Where(Ex{
- "a": "a",
- "b": Op{"neq": "b"},
- "c": Op{"gt": "c"},
- "d": Op{"gte": "d"},
- "e": Op{"lt": "e"},
- "f": Op{"lte": "f"},
- })
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
- `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
-
- b = ds1.Where(Ex{
- "a": From("test2").Select("id"),
- })
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
-}
-
-func (dit *datasetIntegrationTest) TestWhereChain() {
- t := dit.T()
- ds1 := From("test").Where(
- C("x").Eq(0),
- C("y").Eq(1),
- )
-
- ds2 := ds1.Where(
- C("z").Eq(2),
- )
-
- a := ds2.Where(
- C("a").Eq("A"),
- )
- b := ds2.Where(
- C("b").Eq("B"),
- )
- selectSQL, _, err := a.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
- `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("a" = 'A'))`)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
- `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("b" = 'B'))`)
-}
-
-func (dit *datasetIntegrationTest) TestClearWhere() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Eq(1),
- ).ClearWhere()
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestLimit() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).Limit(10)
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) LIMIT 10`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).Limit(0)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
-}
-
-func (dit *datasetIntegrationTest) TestLimitAll() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).LimitAll()
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) LIMIT ALL`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).Limit(0).LimitAll()
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) LIMIT ALL`)
-}
-
-func (dit *datasetIntegrationTest) TestClearLimit() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).LimitAll().ClearLimit()
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).Limit(10).ClearLimit()
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
-}
-
-func (dit *datasetIntegrationTest) TestOffset() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).Offset(10)
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) OFFSET 10`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).Offset(0)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
-}
-
-func (dit *datasetIntegrationTest) TestClearOffset() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).Offset(10).ClearOffset()
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
-}
-
-func (dit *datasetIntegrationTest) TestForUpdate() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).ForUpdate(Wait)
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR UPDATE `)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForUpdate(NoWait)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR UPDATE NOWAIT`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForUpdate(SkipLocked)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR UPDATE SKIP LOCKED`)
-}
-
-func (dit *datasetIntegrationTest) TestForNoKeyUpdate() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).ForNoKeyUpdate(Wait)
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR NO KEY UPDATE `)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForNoKeyUpdate(NoWait)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR NO KEY UPDATE NOWAIT`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForNoKeyUpdate(SkipLocked)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR NO KEY UPDATE SKIP LOCKED`)
-}
-
-func (dit *datasetIntegrationTest) TestForKeyShare() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).ForKeyShare(Wait)
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR KEY SHARE `)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForKeyShare(NoWait)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR KEY SHARE NOWAIT`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForKeyShare(SkipLocked)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR KEY SHARE SKIP LOCKED`)
-}
-
-func (dit *datasetIntegrationTest) TestForShare() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).ForShare(Wait)
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR SHARE `)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForShare(NoWait)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR SHARE NOWAIT`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).ForShare(SkipLocked)
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR SHARE SKIP LOCKED`)
-}
-
-func (dit *datasetIntegrationTest) TestGroupBy() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(
- C("a").Gt(1),
- ).GroupBy("created")
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) GROUP BY "created"`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).GroupBy(L("created::DATE"))
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) GROUP BY created::DATE`)
-
- b = ds1.Where(
- C("a").Gt(1),
- ).GroupBy("name", L("created::DATE"))
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) GROUP BY "name", created::DATE`)
-}
-
-func (dit *datasetIntegrationTest) TestHaving() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Having(Ex{
- "a": Op{"gt": 1},
- }).GroupBy("created")
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" GROUP BY "created" HAVING ("a" > 1)`)
-
- b = ds1.Where(Ex{"b": true}).
- Having(Ex{"a": Op{"gt": 1}}).
- GroupBy("created")
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("b" IS TRUE) GROUP BY "created" HAVING ("a" > 1)`)
-
- b = ds1.Having(Ex{"a": Op{"gt": 1}})
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING ("a" > 1)`)
-
- b = ds1.Having(Ex{"a": Op{"gt": 1}}).Having(Ex{"b": 2})
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING (("a" > 1) AND ("b" = 2))`)
-}
-
-func (dit *datasetIntegrationTest) TestOrder() {
- t := dit.T()
-
- ds1 := From("test")
-
- b := ds1.Order(C("a").Asc(), L(`("a" + "b" > 2)`).Asc())
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
-}
-
-func (dit *datasetIntegrationTest) TestOrderAppend() {
- t := dit.T()
- b := From("test").Order(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
-
- b = From("test").OrderAppend(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
- selectSQL, _, err = b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
-
-}
-
-func (dit *datasetIntegrationTest) TestClearOrder() {
- t := dit.T()
- b := From("test").Order(C("a").Asc().NullsFirst()).ClearOrder()
- selectSQL, _, err := b.ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
-}
-
-func (dit *datasetIntegrationTest) TestJoin() {
- t := dit.T()
- ds1 := From("items")
-
- selectSQL, _, err := ds1.Join(T("players").As("p"), On(Ex{"p.id": I("items.playerId")})).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "players" AS "p" ON ("p"."id" = "items"."playerId")`)
-
- selectSQL, _, err = ds1.Join(ds1.From("players").As("p"), On(Ex{"p.id": I("items.playerId")})).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN (SELECT * FROM "players") AS "p" ON ("p"."id" = "items"."playerId")`)
-
- selectSQL, _, err = ds1.Join(S("v1").Table("test"), On(Ex{"v1.test.id": I("items.playerId")})).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "v1"."test" ON ("v1"."test"."id" = "items"."playerId")`)
-
- selectSQL, _, err = ds1.Join(T("test"), Using(C("name"), C("common_id"))).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
-
- selectSQL, _, err = ds1.Join(T("test"), Using("name", "common_id")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
-
-}
-
-func (dit *datasetIntegrationTest) TestLeftOuterJoin() {
- t := dit.T()
- ds1 := From("items")
-
- selectSQL, _, err := ds1.LeftOuterJoin(T("categories"), On(Ex{
- "categories.categoryId": I("items.id"),
- })).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-
- selectSQL, _, err = ds1.
- LeftOuterJoin(
- T("categories"),
- On(
- I("categories.categoryId").Eq(I("items.id")),
- I("categories.categoryId").In(1, 2, 3)),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, `SELECT * FROM "items" `+
- `LEFT OUTER JOIN "categories" `+
- `ON (("categories"."categoryId" = "items"."id") AND ("categories"."categoryId" IN (1, 2, 3)))`, selectSQL)
-
-}
-
-func (dit *datasetIntegrationTest) TestFullOuterJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.
- FullOuterJoin(T("categories"), On(Ex{"categories.categoryId": I("items.id")})).
- Order(C("stamp").Asc()).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id") ORDER BY "stamp" ASC`)
-
- selectSQL, _, err = ds1.FullOuterJoin(
- T("categories"),
- On(Ex{"categories.categoryId": I("items.id")}),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-}
-
-func (dit *datasetIntegrationTest) TestInnerJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.
- InnerJoin(T("b"), On(Ex{"b.itemsId": I("items.id")})).
- LeftOuterJoin(T("c"), On(Ex{"c.b_id": I("b.id")})).
- ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "b" ON ("b"."itemsId" = "items"."id") `+
- `LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")`)
-
- selectSQL, _, err = ds1.
- InnerJoin(T("b"), On(Ex{"b.itemsId": I("items.id")})).
- LeftOuterJoin(T("c"), On(Ex{"c.b_id": I("b.id")})).
- ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "b" ON ("b"."itemsId" = "items"."id") `+
- `LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")`)
-
- selectSQL, _, err = ds1.InnerJoin(
- T("categories"),
- On(Ex{"categories.categoryId": I("items.id")}),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-}
-
-func (dit *datasetIntegrationTest) TestRightOuterJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.RightOuterJoin(
- T("categories"),
- On(Ex{"categories.categoryId": I("items.id")}),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-}
-
-func (dit *datasetIntegrationTest) TestLeftJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.LeftJoin(
- T("categories"),
- On(Ex{"categories.categoryId": I("items.id")}),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-}
-
-func (dit *datasetIntegrationTest) TestRightJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.RightJoin(
- T("categories"),
- On(Ex{"categories.categoryId": I("items.id")}),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-}
-
-func (dit *datasetIntegrationTest) TestFullJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.FullJoin(
- T("categories"),
- On(Ex{"categories.categoryId": I("items.id")}),
- ).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
-}
-
-func (dit *datasetIntegrationTest) TestNaturalJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.NaturalJoin(T("categories")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL JOIN "categories"`)
-}
-
-func (dit *datasetIntegrationTest) TestNaturalLeftJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.NaturalLeftJoin(T("categories")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL LEFT JOIN "categories"`)
-
-}
-
-func (dit *datasetIntegrationTest) TestNaturalRightJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.NaturalRightJoin(T("categories")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL RIGHT JOIN "categories"`)
-}
-
-func (dit *datasetIntegrationTest) TestNaturalFullJoin() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.NaturalFullJoin(T("categories")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL FULL JOIN "categories"`)
-}
-
-func (dit *datasetIntegrationTest) TestCrossJoin() {
- t := dit.T()
- selectSQL, _, err := From("items").CrossJoin(T("categories")).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" CROSS JOIN "categories"`)
-}
-
-func (dit *datasetIntegrationTest) TestSQLFunctionExpressionsInHaving() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, _, err := ds1.GroupBy("name").Having(SUM("amount").Gt(0)).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM "items" GROUP BY "name" HAVING (SUM("amount") > 0)`)
-}
-
-func (dit *datasetIntegrationTest) TestUnion() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, _, err := a.Union(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Limit(1).Union(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Order(C("id").Asc()).Union(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM `+
- `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Union(b.Limit(1)).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
-
- selectSQL, _, err = a.Union(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
-
- selectSQL, _, err = a.Limit(1).Union(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
-
- selectSQL, _, err = a.Union(b).Union(b.Where(C("id").Lt(50))).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)) `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < 10) AND ("id" < 50)))`)
-
-}
-
-func (dit *datasetIntegrationTest) TestUnionAll() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, _, err := a.UnionAll(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Limit(1).UnionAll(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM `+
- `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Order(C("id").Asc()).UnionAll(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM `+
- `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.UnionAll(b.Limit(1)).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
-
- selectSQL, _, err = a.UnionAll(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `UNION ALL `+
- `(SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
-
- selectSQL, _, err = a.Limit(1).UnionAll(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1"`+
- `)`)
-}
-
-func (dit *datasetIntegrationTest) TestIntersect() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, _, err := a.Intersect(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Limit(1).Intersect(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)`+
- `)`)
-
- selectSQL, _, err = a.Order(C("id").Asc()).Intersect(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
- `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)`+
- `)`)
-
- selectSQL, _, err = a.Intersect(b.Limit(1)).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
-
- selectSQL, _, err = a.Intersect(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `INTERSECT (`+
- `SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1"`+
- `)`)
-
- selectSQL, _, err = a.Limit(1).Intersect(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1"`+
- `)`)
-}
-
-func (dit *datasetIntegrationTest) TestIntersectAll() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, _, err := a.IntersectAll(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
-
- selectSQL, _, err = a.Limit(1).IntersectAll(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)`+
- `)`)
-
- selectSQL, _, err = a.Order(C("id").Asc()).IntersectAll(b).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
- `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)`+
- `)`)
-
- selectSQL, _, err = a.IntersectAll(b.Limit(1)).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
-
- selectSQL, _, err = a.IntersectAll(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
- `INTERSECT ALL `+
- `(SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
-
- selectSQL, _, err = a.Limit(1).IntersectAll(b.Order(C("id").Desc())).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
- `INTERSECT ALL `+
- `(SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
-}
-
-// TO PREPARED
-
-func (dit *datasetIntegrationTest) TestPreparedWhere() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(Ex{
- "a": true,
- "b": Op{"neq": true},
- "c": false,
- "d": Op{"neq": false},
- "e": nil,
- })
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE (`+
- `("a" IS TRUE) `+
- `AND ("b" IS NOT TRUE) `+
- `AND ("c" IS FALSE) `+
- `AND ("d" IS NOT FALSE) `+
- `AND ("e" IS NULL)`+
- `)`)
-
- b = ds1.Where(Ex{
- "a": "a",
- "b": Op{"neq": "b"},
- "c": Op{"gt": "c"},
- "d": Op{"gte": "d"},
- "e": Op{"lt": "e"},
- "f": Op{"lte": "f"},
- "g": Op{"is": nil},
- "h": Op{"isnot": nil},
- })
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{"a", "b", "c", "d", "e", "f"})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE (`+
- `("a" = ?) `+
- `AND ("b" != ?) `+
- `AND ("c" > ?) `+
- `AND ("d" >= ?) `+
- `AND ("e" < ?) `+
- `AND ("f" <= ?) `+
- `AND ("g" IS NULL) `+
- `AND ("h" IS NOT NULL)`+
- `)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedLimit() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(C("a").Gt(1)).Limit(10)
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1), int64(10)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) LIMIT ?`)
-
- b = ds1.Where(C("a").Gt(1)).Limit(0)
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedLimitAll() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(C("a").Gt(1)).LimitAll()
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) LIMIT ALL`)
-
- b = ds1.Where(C("a").Gt(1)).Limit(0).LimitAll()
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) LIMIT ALL`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedClearLimit() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(C("a").Gt(1)).LimitAll().ClearLimit()
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
-
- b = ds1.Where(C("a").Gt(1)).Limit(10).ClearLimit()
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedOffset() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(C("a").Gt(1)).Offset(10)
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1), int64(10)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) OFFSET ?`)
-
- b = ds1.Where(C("a").Gt(1)).Offset(0)
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedClearOffset() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(C("a").Gt(1)).Offset(10).ClearOffset()
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedGroupBy() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Where(C("a").Gt(1)).GroupBy("created")
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) GROUP BY "created"`)
-
- b = ds1.Where(C("a").Gt(1)).GroupBy(L("created::DATE"))
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) GROUP BY created::DATE`)
-
- b = ds1.Where(C("a").Gt(1)).GroupBy("name", L("created::DATE"))
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) GROUP BY "name", created::DATE`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedHaving() {
- t := dit.T()
- ds1 := From("test")
-
- b := ds1.Having(C("a").Gt(1)).GroupBy("created")
- selectSQL, args, err := b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" GROUP BY "created" HAVING ("a" > ?)`)
-
- b = ds1.
- Where(C("b").IsTrue()).
- Having(C("a").Gt(1)).
- GroupBy("created")
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("b" IS TRUE) GROUP BY "created" HAVING ("a" > ?)`)
-
- b = ds1.Having(C("a").Gt(1))
- selectSQL, args, err = b.Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1)})
- assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING ("a" > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedJoin() {
- t := dit.T()
- ds1 := From("items")
-
- selectSQL, args, err := ds1.Join(
- T("players").As("p"),
- On(I("p.id").Eq(I("items.playerId"))),
- ).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "players" AS "p" ON ("p"."id" = "items"."playerId")`)
-
- selectSQL, args, err = ds1.Join(
- ds1.From("players").As("p"),
- On(I("p.id").Eq(I("items.playerId"))),
- ).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN (SELECT * FROM "players") AS "p" ON ("p"."id" = "items"."playerId")`)
-
- selectSQL, args, err = ds1.Join(
- S("v1").Table("test"),
- On(I("v1.test.id").Eq(I("items.playerId"))),
- ).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "v1"."test" ON ("v1"."test"."id" = "items"."playerId")`)
-
- selectSQL, args, err = ds1.Join(
- T("test"),
- Using(I("name"), I("common_id")),
- ).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
-
- selectSQL, args, err = ds1.Join(
- T("test"),
- Using("name", "common_id"),
- ).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
-
- selectSQL, args, err = ds1.Join(
- T("categories"),
- On(
- I("categories.categoryId").Eq(I("items.id")),
- I("categories.categoryId").In(1, 2, 3),
- ),
- ).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1), int64(2), int64(3)})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
- `INNER JOIN "categories" ON (`+
- `("categories"."categoryId" = "items"."id") AND ("categories"."categoryId" IN (?, ?, ?))`+
- `)`)
-
-}
-
-func (dit *datasetIntegrationTest) TestPreparedFunctionExpressionsInHaving() {
- t := dit.T()
- ds1 := From("items")
- selectSQL, args, err := ds1.
- GroupBy("name").
- Having(SUM("amount").Gt(0)).
- Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(0)})
- assert.Equal(t, selectSQL, `SELECT * FROM "items" GROUP BY "name" HAVING (SUM("amount") > ?)`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedUnion() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, args, err := a.Union(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?)`+
- ` UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
-
- selectSQL, args, err = a.Limit(1).Union(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)`+
- `)`)
-
- selectSQL, args, err = a.Union(b.Limit(1)).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
-
- selectSQL, args, err = a.Union(b).Union(b.Where(C("id").Lt(50))).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(10), int64(50)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)) `+
- `UNION (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < ?) AND ("id" < ?)))`)
-
-}
-
-func (dit *datasetIntegrationTest) TestPreparedUnionAll() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, args, err := a.UnionAll(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
-
- selectSQL, args, err = a.Limit(1).UnionAll(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)`+
- `)`)
-
- selectSQL, args, err = a.UnionAll(b.Limit(1)).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
-
- selectSQL, args, err = a.UnionAll(b).UnionAll(b.Where(C("id").Lt(50))).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(10), int64(50)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)) `+
- `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < ?) AND ("id" < ?)))`)
-}
-
-func (dit *datasetIntegrationTest) TestPreparedIntersect() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, args, err := a.Intersect(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
-
- selectSQL, args, err = a.Limit(1).Intersect(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
- `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)`+
- `)`)
-
- selectSQL, args, err = a.Intersect(b.Limit(1)).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
-
-}
-
-func (dit *datasetIntegrationTest) TestPreparedIntersectAll() {
- t := dit.T()
- a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
- b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
-
- selectSQL, args, err := a.IntersectAll(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
- `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
-
- selectSQL, args, err = a.Limit(1).IntersectAll(b).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
- assert.Equal(t, selectSQL, `SELECT * FROM (`+
- `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
- `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)`+
- `)`)
-
- selectSQL, args, err = a.IntersectAll(b.Limit(1)).Prepared(true).ToSQL()
- assert.NoError(t, err)
- assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
- assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?)`+
- ` INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
-
-}
-
-func TestDatasetIntegrationSuite(t *testing.T) {
- suite.Run(t, new(datasetIntegrationTest))
-}
diff --git a/dataset_test.go b/dataset_test.go
deleted file mode 100644
index c969751f..00000000
--- a/dataset_test.go
+++ /dev/null
@@ -1,829 +0,0 @@
-package goqu
-
-import (
- "testing"
-
- "github.com/doug-martin/goqu/v7/exp"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/sb"
- "github.com/doug-martin/goqu/v7/mocks"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
-)
-
-type datasetTest struct {
- suite.Suite
-}
-
-func (dt *datasetTest) TestClone() {
- t := dt.T()
- ds := From("test")
- assert.Equal(t, ds.Clone(), ds)
-}
-
-func (dt *datasetTest) TestExpression() {
- t := dt.T()
- ds := From("test")
- assert.Equal(t, ds.Expression(), ds)
-}
-
-func (dt *datasetTest) TestDialect() {
- t := dt.T()
- ds := From("test")
- assert.NotNil(t, ds.Dialect())
-}
-
-func (dt *datasetTest) TestWithDialect() {
- t := dt.T()
- ds := From("test")
- dialect := GetDialect("default")
- ds.WithDialect("default")
- assert.Equal(t, ds.Dialect(), dialect)
-}
-
-func (dt *datasetTest) TestPrepared() {
- t := dt.T()
- ds := From("test")
- preparedDs := ds.Prepared(true)
- assert.True(t, preparedDs.IsPrepared())
- assert.False(t, ds.IsPrepared())
- // should apply the prepared to any datasets created from the root
- assert.True(t, preparedDs.Where(Ex{"a": 1}).IsPrepared())
-}
-
-func (dt *datasetTest) TestGetClauses() {
- t := dt.T()
- ds := From("test")
- ce := exp.NewClauses().SetFrom(exp.NewColumnListExpression(I("test")))
- assert.Equal(t, ce, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestWith() {
- t := dt.T()
- from := From("cte")
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from))
- assert.Equal(t, ec, ds.With("test-cte", from).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestWithRecursive() {
- t := dt.T()
- from := From("cte")
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from))
- assert.Equal(t, ec, ds.WithRecursive("test-cte", from).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestSelect(selects ...interface{}) {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetSelect(exp.NewColumnListExpression(C("a")))
- assert.Equal(t, ec, ds.Select(C("a")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestSelectDistinct(selects ...interface{}) {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetSelectDistinct(exp.NewColumnListExpression(C("a")))
- assert.Equal(t, ec, ds.SelectDistinct(C("a")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestClearSelect() {
- t := dt.T()
- ds := From("test").Select(C("a"))
- dsc := ds.GetClauses()
- ec := dsc.SetSelect(exp.NewColumnListExpression(Star()))
- assert.Equal(t, ec, ds.ClearSelect().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestSelectAppend(selects ...interface{}) {
- t := dt.T()
- ds := From("test").Select(C("a"))
- dsc := ds.GetClauses()
- ec := dsc.SelectAppend(exp.NewColumnListExpression(C("b")))
- assert.Equal(t, ec, ds.SelectAppend(C("b")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestFrom(from ...interface{}) {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetFrom(exp.NewColumnListExpression(T("t")))
- assert.Equal(t, ec, ds.From(T("t")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestFromSelf() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetFrom(exp.NewColumnListExpression(ds.As("t1")))
- assert.Equal(t, ec, ds.FromSelf().GetClauses())
-
- ec2 := dsc.SetFrom(exp.NewColumnListExpression(ds.As("test")))
- assert.Equal(t, ec2, ds.As("test").FromSelf().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestCompoundFromSelf() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- assert.Equal(t, dsc, ds.CompoundFromSelf().GetClauses())
-
- ds2 := ds.Limit(1)
- dsc2 := exp.NewClauses().SetFrom(exp.NewColumnListExpression(ds2.As("t1")))
- assert.Equal(t, dsc2, ds2.CompoundFromSelf().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.InnerJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.Join(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestInnerJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.InnerJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.InnerJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestFullOuterJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.FullOuterJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.FullOuterJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestRightOuterJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.RightOuterJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.RightOuterJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestLeftOuterJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.LeftOuterJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.LeftOuterJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestFullJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.FullJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.FullJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestRightJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.RightJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.RightJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestLeftJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewConditionedJoinExpression(exp.LeftJoinType, T("foo"), On(C("a").IsNull())),
- )
- assert.Equal(t, ec, ds.LeftJoin(T("foo"), On(C("a").IsNull())).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestNaturalJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, T("foo")),
- )
- assert.Equal(t, ec, ds.NaturalJoin(T("foo")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestNaturalLeftJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewUnConditionedJoinExpression(exp.NaturalLeftJoinType, T("foo")),
- )
- assert.Equal(t, ec, ds.NaturalLeftJoin(T("foo")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestNaturalRightJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewUnConditionedJoinExpression(exp.NaturalRightJoinType, T("foo")),
- )
- assert.Equal(t, ec, ds.NaturalRightJoin(T("foo")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-func (dt *datasetTest) TestNaturalFullJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewUnConditionedJoinExpression(exp.NaturalFullJoinType, T("foo")),
- )
- assert.Equal(t, ec, ds.NaturalFullJoin(T("foo")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestCrossJoin() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.JoinsAppend(
- exp.NewUnConditionedJoinExpression(exp.CrossJoinType, T("foo")),
- )
- assert.Equal(t, ec, ds.CrossJoin(T("foo")).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestWhere() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- w := Ex{
- "a": 1,
- }
- ec := dsc.WhereAppend(w)
- assert.Equal(t, ec, ds.Where(w).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestClearWhere() {
- t := dt.T()
- w := Ex{
- "a": 1,
- }
- ds := From("test").Where(w)
- dsc := ds.GetClauses()
- ec := dsc.ClearWhere()
- assert.Equal(t, ec, ds.ClearWhere().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestForUpdate() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetLock(exp.NewLock(exp.ForUpdate, NoWait))
- assert.Equal(t, ec, ds.ForUpdate(NoWait).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestForNoKeyUpdate() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetLock(exp.NewLock(exp.ForNoKeyUpdate, NoWait))
- assert.Equal(t, ec, ds.ForNoKeyUpdate(NoWait).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestForKeyShare() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetLock(exp.NewLock(exp.ForKeyShare, NoWait))
- assert.Equal(t, ec, ds.ForKeyShare(NoWait).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestForShare() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetLock(exp.NewLock(exp.ForShare, NoWait))
- assert.Equal(t, ec, ds.ForShare(NoWait).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestGroupBy() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetGroupBy(exp.NewColumnListExpression(C("a")))
- assert.Equal(t, ec, ds.GroupBy("a").GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestHaving() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- h := C("a").Gt(1)
- ec := dsc.HavingAppend(h)
- assert.Equal(t, ec, ds.Having(h).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestOrder() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- o := C("a").Desc()
- ec := dsc.SetOrder(o)
- assert.Equal(t, ec, ds.Order(o).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestOrderAppend() {
- t := dt.T()
- ds := From("test").Order(C("a").Desc())
- dsc := ds.GetClauses()
- o := C("b").Desc()
- ec := dsc.OrderAppend(o)
- assert.Equal(t, ec, ds.OrderAppend(o).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestClearOrder() {
- t := dt.T()
- ds := From("test").Order(C("a").Desc())
- dsc := ds.GetClauses()
- ec := dsc.ClearOrder()
- assert.Equal(t, ec, ds.ClearOrder().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestLimit() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetLimit(uint(1))
- assert.Equal(t, ec, ds.Limit(1).GetClauses())
- assert.Equal(t, dsc, ds.Limit(0).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestLimitAll() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetLimit(L("ALL"))
- assert.Equal(t, ec, ds.LimitAll().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestClearLimit() {
- t := dt.T()
- ds := From("test").Limit(1)
- dsc := ds.GetClauses()
- ec := dsc.ClearLimit()
- assert.Equal(t, ec, ds.ClearLimit().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestOffset() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetOffset(1)
- assert.Equal(t, ec, ds.Offset(1).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestClearOffset() {
- t := dt.T()
- ds := From("test").Offset(1)
- dsc := ds.GetClauses()
- ec := dsc.ClearOffset()
- assert.Equal(t, ec, ds.ClearOffset().GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestUnion() {
- t := dt.T()
- uds := From("union_test")
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.UnionCompoundType, uds))
- assert.Equal(t, ec, ds.Union(uds).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestUnionAll() {
- t := dt.T()
- uds := From("union_test")
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.UnionAllCompoundType, uds))
- assert.Equal(t, ec, ds.UnionAll(uds).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestIntersect() {
- t := dt.T()
- uds := From("union_test")
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.IntersectCompoundType, uds))
- assert.Equal(t, ec, ds.Intersect(uds).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-func (dt *datasetTest) TestIntersectAll() {
- t := dt.T()
- uds := From("union_test")
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.IntersectAllCompoundType, uds))
- assert.Equal(t, ec, ds.IntersectAll(uds).GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestReturning() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetReturning(exp.NewColumnListExpression(C("a")))
- assert.Equal(t, ec, ds.Returning("a").GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestAs() {
- t := dt.T()
- ds := From("test")
- dsc := ds.GetClauses()
- ec := dsc.SetAlias(T("a"))
- assert.Equal(t, ec, ds.As("a").GetClauses())
- assert.Equal(t, dsc, ds.GetClauses())
-}
-
-func (dt *datasetTest) TestToSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToSelectSQL", sqlB, c).Return(nil).Once()
- sql, args, err := ds.ToSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToSQL_ReturnedError() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- ee := errors.New("expected error")
- md.On("ToSelectSQL", sqlB, c).Run(func(args mock.Arguments) {
- args.Get(0).(sb.SQLBuilder).SetError(ee)
- }).Once()
-
- sql, args, err := ds.ToSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Equal(t, ee, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestAppendSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToSelectSQL", sqlB, c).Return(nil).Once()
- ds.AppendSQL(sqlB)
- assert.NoError(t, sqlB.Error())
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToInsertSQL_WithNoArgs() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- eie, err := exp.NewInsertExpression()
- assert.NoError(t, err)
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToInsertSQL", sqlB, c, eie).Return(nil).Once()
- sql, args, err := ds.ToInsertSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToInsertSQL_WithReturnedError() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- rows := []interface{}{
- Record{"c": "a"},
- Record{"c": "b"},
- }
- eie, err := exp.NewInsertExpression(rows...)
- assert.NoError(t, err)
-
- sqlB := sb.NewSQLBuilder(false)
- ee := errors.New("test")
- md.On("ToInsertSQL", sqlB, c, eie).Run(func(args mock.Arguments) {
- args.Get(0).(sb.SQLBuilder).SetError(ee)
- }).Once()
- sql, args, err := ds.ToInsertSQL(rows...)
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Equal(t, ee, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToInsertIgnoreSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- rows := []interface{}{
- Record{"c": "a"},
- Record{"c": "b"},
- }
- eie, err := exp.NewInsertExpression(rows...)
- assert.NoError(t, err)
- eie = eie.DoNothing()
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToInsertSQL", sqlB, c, eie).Return(nil).Once()
- sql, args, err := ds.ToInsertIgnoreSQL(rows...)
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToInsertConflictSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- ce := DoUpdate("a", "b")
- rows := []interface{}{
- Record{"c": "a"},
- Record{"c": "b"},
- }
- eie, err := exp.NewInsertExpression(rows...)
- assert.NoError(t, err)
- sqlB := sb.NewSQLBuilder(false)
- eie = eie.SetOnConflict(ce)
- md.On("ToInsertSQL", sqlB, c, eie).Return(nil).Once()
- sql, args, err := ds.ToInsertConflictSQL(ce, rows...)
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToUpdateSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- r := Record{"c": "a"}
- md.On("ToUpdateSQL", sqlB, c, r).Return(nil).Once()
- sql, args, err := ds.ToUpdateSQL(Record{"c": "a"})
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToUpdateSQL_Prepared() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").Prepared(true).SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(true)
- r := Record{"c": "a"}
- md.On("ToUpdateSQL", sqlB, c, r).Return(nil).Once()
- sql, args, err := ds.ToUpdateSQL(Record{"c": "a"})
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToUpdateSQL_WithError() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- r := Record{"c": "a"}
- ee := errors.New("expected error")
- md.On("ToUpdateSQL", sqlB, c, r).Run(func(args mock.Arguments) {
- args.Get(0).(sb.SQLBuilder).SetError(ee)
- }).Once()
-
- sql, args, err := ds.ToUpdateSQL(Record{"c": "a"})
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Equal(t, ee, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToDeleteSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToDeleteSQL", sqlB, c).Return(nil).Once()
-
- sql, args, err := ds.ToDeleteSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToDeleteSQL_Prepared() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").Prepared(true).SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(true)
- md.On("ToDeleteSQL", sqlB, c).Return(nil).Once()
-
- sql, args, err := ds.ToDeleteSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToDeleteSQL_WithError() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- ee := errors.New("expected error")
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToDeleteSQL", sqlB, c).Run(func(args mock.Arguments) {
- args.Get(0).(sb.SQLBuilder).SetError(ee)
- }).Once()
-
- sql, args, err := ds.ToDeleteSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Equal(t, ee, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToTruncateSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToTruncateSQL", sqlB, c, TruncateOptions{}).Return(nil).Once()
-
- sql, args, err := ds.ToTruncateSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToTruncateSQL__Prepared() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").Prepared(true).SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(true)
- md.On("ToTruncateSQL", sqlB, c, TruncateOptions{}).Return(nil).Once()
-
- sql, args, err := ds.ToTruncateSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToTruncateSQL_WithError() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- ee := errors.New("expected error")
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToTruncateSQL", sqlB, c, TruncateOptions{}).Run(func(args mock.Arguments) {
- args.Get(0).(sb.SQLBuilder).SetError(ee)
- }).Once()
-
- sql, args, err := ds.ToTruncateSQL()
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Equal(t, ee, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToTruncateWithOptsSQL() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(false)
- to := TruncateOptions{Cascade: true}
- md.On("ToTruncateSQL", sqlB, c, to).Return(nil).Once()
-
- sql, args, err := ds.ToTruncateWithOptsSQL(to)
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToTruncateWithOptsSQL_Prepared() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").Prepared(true).SetDialect(md)
- c := ds.GetClauses()
- sqlB := sb.NewSQLBuilder(true)
- to := TruncateOptions{Cascade: true}
- md.On("ToTruncateSQL", sqlB, c, to).Return(nil).Once()
-
- sql, args, err := ds.ToTruncateWithOptsSQL(to)
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Nil(t, err)
- md.AssertExpectations(t)
-}
-
-func (dt *datasetTest) TestToTruncateWithOptsSQL_WithError() {
- t := dt.T()
- md := new(mocks.SQLDialect)
- ds := From("test").SetDialect(md)
- c := ds.GetClauses()
- ee := errors.New("expected error")
- to := TruncateOptions{Cascade: true}
- sqlB := sb.NewSQLBuilder(false)
- md.On("ToTruncateSQL", sqlB, c, to).Run(func(args mock.Arguments) {
- args.Get(0).(sb.SQLBuilder).SetError(ee)
- }).Once()
-
- sql, args, err := ds.ToTruncateWithOptsSQL(to)
- assert.Empty(t, sql)
- assert.Empty(t, args)
- assert.Equal(t, ee, err)
- md.AssertExpectations(t)
-}
-
-func TestDatasetSuite(t *testing.T) {
- suite.Run(t, new(datasetTest))
-}
diff --git a/delete_dataset.go b/delete_dataset.go
new file mode 100644
index 00000000..6cb32e17
--- /dev/null
+++ b/delete_dataset.go
@@ -0,0 +1,191 @@
+package goqu
+
+import (
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+)
+
+type DeleteDataset struct {
+ dialect SQLDialect
+ clauses exp.DeleteClauses
+ isPrepared bool
+ queryFactory exec.QueryFactory
+}
+
+// used internally by database to create a database with a specific adapter
+func newDeleteDataset(d string, queryFactory exec.QueryFactory) *DeleteDataset {
+ return &DeleteDataset{
+ clauses: exp.NewDeleteClauses(),
+ dialect: GetDialect(d),
+ queryFactory: queryFactory,
+ }
+}
+
+func Delete(table interface{}) *DeleteDataset {
+ return newDeleteDataset("default", nil).From(table)
+}
+
+// Set the parameter interpolation behavior. See examples
+//
+// 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
+ return ret
+}
+
+// Returns true if Prepared(true) has been called on this dataset
+func (dd *DeleteDataset) IsPrepared() bool {
+ return dd.isPrepared
+}
+
+// Sets the adapter used to serialize values and create the SQL statement
+func (dd *DeleteDataset) WithDialect(dl string) *DeleteDataset {
+ ds := dd.copy(dd.GetClauses())
+ ds.dialect = GetDialect(dl)
+ return ds
+}
+
+// Returns the current SQLDialect on the dataset
+func (dd *DeleteDataset) Dialect() SQLDialect {
+ return dd.dialect
+}
+
+// Set the dialect for this dataset.
+func (dd *DeleteDataset) SetDialect(dialect SQLDialect) *DeleteDataset {
+ cd := dd.copy(dd.GetClauses())
+ cd.dialect = dialect
+ return cd
+}
+
+// Returns the current clauses on the dataset.
+func (dd *DeleteDataset) GetClauses() exp.DeleteClauses {
+ return dd.clauses
+}
+
+// used interally to copy the dataset
+func (dd *DeleteDataset) copy(clauses exp.DeleteClauses) *DeleteDataset {
+ return &DeleteDataset{
+ dialect: dd.dialect,
+ clauses: clauses,
+ isPrepared: dd.isPrepared,
+ queryFactory: dd.queryFactory,
+ }
+}
+
+// Creates a WITH clause for a common table expression (CTE).
+//
+// The name will be available to SELECT from in the associated query; and can optionally
+// contain a list of column names "name(col1, col2, col3)".
+//
+// The name will refer to the results of the specified subquery.
+func (dd *DeleteDataset) With(name string, subquery exp.Expression) *DeleteDataset {
+ return dd.copy(dd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
+}
+
+// Creates a WITH RECURSIVE clause for a common table expression (CTE)
+//
+// The name will be available to SELECT from in the associated query; and must
+// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
+//
+// The name will refer to the results of the specified subquery. The subquery for
+// a recursive query will always end with a UNION or UNION ALL with a clause that
+// refers to the CTE by name.
+func (dd *DeleteDataset) WithRecursive(name string, subquery exp.Expression) *DeleteDataset {
+ return dd.copy(dd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
+}
+
+// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
+// LiteralExpression: (See Literal) Will use the literal SQL
+func (dd *DeleteDataset) From(table interface{}) *DeleteDataset {
+ switch t := table.(type) {
+ case exp.IdentifierExpression:
+ return dd.copy(dd.clauses.SetFrom(t))
+ case string:
+ return dd.copy(dd.clauses.SetFrom(exp.ParseIdentifier(t)))
+ default:
+ panic("unsupported table type, a string or identifier expression is required")
+ }
+}
+
+// Adds a WHERE clause. See examples.
+func (dd *DeleteDataset) Where(expressions ...exp.Expression) *DeleteDataset {
+ return dd.copy(dd.clauses.WhereAppend(expressions...))
+}
+
+// Removes the WHERE clause. See examples.
+func (dd *DeleteDataset) ClearWhere() *DeleteDataset {
+ return dd.copy(dd.clauses.ClearWhere())
+}
+
+// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
+func (dd *DeleteDataset) Order(order ...exp.OrderedExpression) *DeleteDataset {
+ return dd.copy(dd.clauses.SetOrder(order...))
+}
+
+// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
+// calling Order. See examples.
+func (dd *DeleteDataset) OrderAppend(order ...exp.OrderedExpression) *DeleteDataset {
+ return dd.copy(dd.clauses.OrderAppend(order...))
+}
+
+// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
+// calling Order. See examples.
+func (dd *DeleteDataset) OrderPrepend(order ...exp.OrderedExpression) *DeleteDataset {
+ return dd.copy(dd.clauses.OrderPrepend(order...))
+}
+
+// Removes the ORDER BY clause. See examples.
+func (dd *DeleteDataset) ClearOrder() *DeleteDataset {
+ return dd.copy(dd.clauses.ClearOrder())
+}
+
+// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
+func (dd *DeleteDataset) Limit(limit uint) *DeleteDataset {
+ if limit > 0 {
+ return dd.copy(dd.clauses.SetLimit(limit))
+ }
+ return dd.copy(dd.clauses.ClearLimit())
+}
+
+// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
+func (dd *DeleteDataset) LimitAll() *DeleteDataset {
+ return dd.copy(dd.clauses.SetLimit(L("ALL")))
+}
+
+// Removes the LIMIT clause.
+func (dd *DeleteDataset) ClearLimit() *DeleteDataset {
+ return dd.copy(dd.clauses.ClearLimit())
+}
+
+// Adds a RETURNING clause to the dataset if the adapter supports it.
+func (dd *DeleteDataset) Returning(returning ...interface{}) *DeleteDataset {
+ return dd.copy(dd.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
+}
+
+// Generates a DELETE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
+// See examples.
+//
+// Errors:
+// * There is an error generating the SQL
+func (dd *DeleteDataset) ToSQL() (sql string, params []interface{}, err error) {
+ return dd.deleteSQLBuilder().ToSQL()
+}
+
+// Creates an QueryExecutor to execute the query.
+// db.Delete("test").Exec()
+//
+// See Dataset#ToUpdateSQL for arguments
+func (dd *DeleteDataset) Executor() exec.QueryExecutor {
+ return dd.queryFactory.FromSQLBuilder(dd.deleteSQLBuilder())
+}
+
+func (dd *DeleteDataset) deleteSQLBuilder() sb.SQLBuilder {
+ buf := sb.NewSQLBuilder(dd.isPrepared)
+ dd.dialect.ToDeleteSQL(buf, dd.clauses)
+ return buf
+}
diff --git a/delete_dataset_example_test.go b/delete_dataset_example_test.go
new file mode 100644
index 00000000..2d1716bc
--- /dev/null
+++ b/delete_dataset_example_test.go
@@ -0,0 +1,313 @@
+package goqu_test
+
+import (
+ "fmt"
+
+ "github.com/doug-martin/goqu/v8"
+ _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+)
+
+func ExampleDelete() {
+ ds := goqu.Delete("items")
+
+ sql, args, _ := ds.ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // DELETE FROM "items" []
+}
+
+func ExampleDeleteDataset_Executor() {
+ db := getDb()
+
+ de := db.Delete("goqu_user").
+ Where(goqu.Ex{"first_name": "Bob"}).
+ Executor()
+ if r, err := de.Exec(); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ c, _ := r.RowsAffected()
+ fmt.Printf("Deleted %d users", c)
+ }
+
+ // Output:
+ // Deleted 1 users
+}
+
+func ExampleDeleteDataset_Executor_returning() {
+ db := getDb()
+
+ de := db.Delete("goqu_user").
+ Where(goqu.C("last_name").Eq("Yukon")).
+ Returning(goqu.C("id")).
+ Executor()
+
+ var ids []int64
+ if err := de.ScanVals(&ids); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Printf("Deleted users [ids:=%+v]", ids)
+ }
+
+ // Output:
+ // Deleted users [ids:=[1 2 3]]
+}
+
+func ExampleDeleteDataset_With() {
+
+ sql, _, _ := goqu.Delete("test").
+ With("check_vals(val)", goqu.From().Select(goqu.L("123"))).
+ Where(goqu.C("val").Eq(goqu.From("check_vals").Select("val"))).
+ ToSQL()
+ fmt.Println(sql)
+
+ // Output:
+ // WITH check_vals(val) AS (SELECT 123) DELETE FROM "test" WHERE ("val" IN (SELECT "val" FROM "check_vals"))
+}
+
+func ExampleDeleteDataset_WithRecursive() {
+ sql, _, _ := goqu.Delete("nums").
+ WithRecursive("nums(x)",
+ goqu.From().Select(goqu.L("1")).
+ UnionAll(goqu.From("nums").
+ Select(goqu.L("x+1")).Where(goqu.C("x").Lt(5)))).
+ ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // WITH RECURSIVE nums(x) AS (SELECT 1 UNION ALL (SELECT x+1 FROM "nums" WHERE ("x" < 5))) DELETE FROM "nums"
+}
+
+func ExampleDeleteDataset_Where() {
+ // By default everything is anded together
+ sql, _, _ := goqu.Delete("test").Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql)
+ // You can use ExOr to get ORed expressions together
+ sql, _, _ = goqu.Delete("test").Where(goqu.ExOr{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql)
+ // You can use Or with Ex to Or multiple Ex maps together
+ sql, _, _ = goqu.Delete("test").Where(
+ goqu.Or(
+ goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ },
+ goqu.Ex{
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ },
+ ),
+ ).ToSQL()
+ fmt.Println(sql)
+ // By default everything is anded together
+ sql, _, _ = goqu.Delete("test").Where(
+ goqu.C("a").Gt(10),
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ goqu.C("d").In("a", "b", "c"),
+ ).ToSQL()
+ fmt.Println(sql)
+ // You can use a combination of Ors and Ands
+ sql, _, _ = goqu.Delete("test").Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+ ).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM "test" WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+ // DELETE FROM "test" WHERE (("a" > 10) OR ("b" < 10) OR ("c" IS NULL) OR ("d" IN ('a', 'b', 'c')))
+ // DELETE FROM "test" WHERE ((("a" > 10) AND ("b" < 10)) OR (("c" IS NULL) AND ("d" IN ('a', 'b', 'c'))))
+ // DELETE FROM "test" WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+ // DELETE FROM "test" WHERE (("a" > 10) OR (("b" < 10) AND ("c" IS NULL)))
+}
+
+func ExampleDeleteDataset_Where_prepared() {
+ // By default everything is anded together
+ sql, args, _ := goqu.Delete("test").Prepared(true).Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql, args)
+ // You can use ExOr to get ORed expressions together
+ sql, args, _ = goqu.Delete("test").Prepared(true).Where(goqu.ExOr{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql, args)
+ // You can use Or with Ex to Or multiple Ex maps together
+ sql, args, _ = goqu.Delete("test").Prepared(true).Where(
+ goqu.Or(
+ goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ },
+ goqu.Ex{
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ },
+ ),
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // By default everything is anded together
+ sql, args, _ = goqu.Delete("test").Prepared(true).Where(
+ goqu.C("a").Gt(10),
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ goqu.C("d").In("a", "b", "c"),
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // You can use a combination of Ors and Ands
+ sql, args, _ = goqu.Delete("test").Prepared(true).Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // Output:
+ // DELETE FROM "test" WHERE (("a" > ?) AND ("b" < ?) AND ("c" IS NULL) AND ("d" IN (?, ?, ?))) [10 10 a b c]
+ // DELETE FROM "test" WHERE (("a" > ?) OR ("b" < ?) OR ("c" IS NULL) OR ("d" IN (?, ?, ?))) [10 10 a b c]
+ // DELETE FROM "test" WHERE ((("a" > ?) AND ("b" < ?)) OR (("c" IS NULL) AND ("d" IN (?, ?, ?)))) [10 10 a b c]
+ // DELETE FROM "test" WHERE (("a" > ?) AND ("b" < ?) AND ("c" IS NULL) AND ("d" IN (?, ?, ?))) [10 10 a b c]
+ // DELETE FROM "test" WHERE (("a" > ?) OR (("b" < ?) AND ("c" IS NULL))) [10 10]
+}
+
+func ExampleDeleteDataset_ClearWhere() {
+ ds := goqu.Delete("test").Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+ )
+ sql, _, _ := ds.ClearWhere().ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM "test"
+}
+
+func ExampleDeleteDataset_Limit() {
+ ds := goqu.Dialect("mysql").Delete("test").Limit(10)
+ sql, _, _ := ds.ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM `test` LIMIT 10
+}
+
+func ExampleDeleteDataset_LimitAll() {
+ // Using mysql dialect because it supports limit on delete
+ ds := goqu.Dialect("mysql").Delete("test").LimitAll()
+ sql, _, _ := ds.ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM `test` LIMIT ALL
+}
+
+func ExampleDeleteDataset_ClearLimit() {
+ // Using mysql dialect because it supports limit on delete
+ ds := goqu.Dialect("mysql").Delete("test").Limit(10)
+ sql, _, _ := ds.ClearLimit().ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM `test`
+}
+
+func ExampleDeleteDataset_Order() {
+ // use mysql dialect because it supports order by on deletes
+ ds := goqu.Dialect("mysql").Delete("test").Order(goqu.C("a").Asc())
+ sql, _, _ := ds.ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM `test` ORDER BY `a` ASC
+}
+
+func ExampleDeleteDataset_OrderAppend() {
+ // use mysql dialect because it supports order by on deletes
+ ds := goqu.Dialect("mysql").Delete("test").Order(goqu.C("a").Asc())
+ sql, _, _ := ds.OrderAppend(goqu.C("b").Desc().NullsLast()).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM `test` ORDER BY `a` ASC, `b` DESC NULLS LAST
+}
+
+func ExampleDeleteDataset_OrderPrepend() {
+ // use mysql dialect because it supports order by on deletes
+ ds := goqu.Dialect("mysql").Delete("test").Order(goqu.C("a").Asc())
+ sql, _, _ := ds.OrderPrepend(goqu.C("b").Desc().NullsLast()).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM `test` ORDER BY `b` DESC NULLS LAST, `a` ASC
+}
+
+func ExampleDeleteDataset_ClearOrder() {
+ ds := goqu.Delete("test").Order(goqu.C("a").Asc())
+ sql, _, _ := ds.ClearOrder().ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // DELETE FROM "test"
+}
+
+func ExampleDeleteDataset_ToSQL() {
+ sql, args, _ := goqu.Delete("items").ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = goqu.Delete("items").
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // DELETE FROM "items" []
+ // DELETE FROM "items" WHERE ("id" > 10) []
+}
+
+func ExampleDeleteDataset_Prepared() {
+ sql, args, _ := goqu.Delete("items").Prepared(true).ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = goqu.Delete("items").
+ Prepared(true).
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // DELETE FROM "items" []
+ // DELETE FROM "items" WHERE ("id" > ?) [10]
+}
+
+func ExampleDeleteDataset_Returning() {
+ ds := goqu.Delete("items")
+ sql, args, _ := ds.Returning("id").ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = ds.Returning("id").Where(goqu.C("id").IsNotNull()).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // DELETE FROM "items" RETURNING "id" []
+ // DELETE FROM "items" WHERE ("id" IS NOT NULL) RETURNING "id" []
+}
diff --git a/delete_dataset_test.go b/delete_dataset_test.go
new file mode 100644
index 00000000..cf933faf
--- /dev/null
+++ b/delete_dataset_test.go
@@ -0,0 +1,572 @@
+package goqu
+
+import (
+ "testing"
+
+ "github.com/DATA-DOG/go-sqlmock"
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+ "github.com/doug-martin/goqu/v8/mocks"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+)
+
+type deleteDatasetSuite struct {
+ suite.Suite
+}
+
+func (dds *deleteDatasetSuite) SetupSuite() {
+ noReturn := DefaultDialectOptions()
+ noReturn.SupportsReturn = false
+ RegisterDialect("no-return", noReturn)
+
+ limitOnDelete := DefaultDialectOptions()
+ limitOnDelete.SupportsLimitOnDelete = true
+ RegisterDialect("limit-on-delete", limitOnDelete)
+
+ orderOnDelete := DefaultDialectOptions()
+ orderOnDelete.SupportsOrderByOnDelete = true
+ RegisterDialect("order-on-delete", orderOnDelete)
+}
+
+func (dds *deleteDatasetSuite) TearDownSuite() {
+ DeregisterDialect("no-return")
+ DeregisterDialect("limit-on-delete")
+ DeregisterDialect("order-on-delete")
+}
+
+func (dds *deleteDatasetSuite) TestDialect() {
+ t := dds.T()
+ ds := Delete("test")
+ assert.NotNil(t, ds.Dialect())
+}
+
+func (dds *deleteDatasetSuite) TestWithDialect() {
+ t := dds.T()
+ ds := Delete("test")
+ dialect := GetDialect("default")
+ ds.WithDialect("default")
+ assert.Equal(t, ds.Dialect(), dialect)
+}
+
+func (dds *deleteDatasetSuite) TestPrepared() {
+ t := dds.T()
+ ds := Delete("test")
+ preparedDs := ds.Prepared(true)
+ assert.True(t, preparedDs.IsPrepared())
+ assert.False(t, ds.IsPrepared())
+ // should apply the prepared to any datasets created from the root
+ assert.True(t, preparedDs.Where(Ex{"a": 1}).IsPrepared())
+}
+
+func (dds *deleteDatasetSuite) TestPrepared_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("items")
+ dsql, args, err := ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{})
+ assert.Equal(t, `DELETE FROM "items"`, dsql)
+
+ dsql, args, err = ds1.Where(I("id").Eq(1)).Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1)})
+ assert.Equal(t, `DELETE FROM "items" WHERE ("id" = ?)`, dsql)
+
+ dsql, args, err = ds1.Returning("id").Where(I("id").Eq(1)).Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1)})
+ assert.Equal(t, `DELETE FROM "items" WHERE ("id" = ?) RETURNING "id"`, dsql)
+}
+
+func (dds *deleteDatasetSuite) TestGetClauses() {
+ t := dds.T()
+ ds := Delete("test")
+ ce := exp.NewDeleteClauses().SetFrom(I("test"))
+ assert.Equal(t, ce, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestWith() {
+ t := dds.T()
+ from := From("cte")
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from))
+ assert.Equal(t, ec, ds.With("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestWithRecursive() {
+ t := dds.T()
+ from := From("cte")
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from))
+ assert.Equal(t, ec, ds.WithRecursive("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestFrom() {
+ t := dds.T()
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetFrom(T("t"))
+ assert.Equal(t, ec, ds.From(T("t")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestFrom_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("test")
+
+ deleteSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test"`)
+
+ ds2 := ds1.From("test2")
+ deleteSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test2"`)
+
+ // original should not change
+ deleteSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test"`)
+
+}
+
+func (dds *deleteDatasetSuite) TestWhere() {
+ t := dds.T()
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ w := Ex{
+ "a": 1,
+ }
+ ec := dsc.WhereAppend(w)
+ assert.Equal(t, ec, ds.Where(w).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestWhere_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("test")
+
+ b := ds1.Where(
+ C("a").Eq(true),
+ C("a").Neq(true),
+ C("a").Eq(false),
+ C("a").Neq(false),
+ )
+ deleteSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
+
+ b = ds1.Where(
+ C("a").Eq("a"),
+ C("b").Neq("b"),
+ C("c").Gt("c"),
+ C("d").Gte("d"),
+ C("e").Lt("e"),
+ C("f").Lte("f"),
+ )
+ deleteSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a", "b", "c", "d", "e", "f"}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("a" = ?) AND ("b" != ?) AND ("c" > ?) AND ("d" >= ?) AND ("e" < ?) AND ("f" <= ?))`)
+
+ b = ds1.Where(
+ C("a").Eq(From("test2").Select("id")),
+ )
+ deleteSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ b = ds1.Where(Ex{
+ "a": "a",
+ "b": Op{"neq": "b"},
+ "c": Op{"gt": "c"},
+ "d": Op{"gte": "d"},
+ "e": Op{"lt": "e"},
+ "f": Op{"lte": "f"},
+ })
+ deleteSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a", "b", "c", "d", "e", "f"}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("a" = ?) AND ("b" != ?) AND ("c" > ?) AND ("d" >= ?) AND ("e" < ?) AND ("f" <= ?))`)
+
+ b = ds1.Where(Ex{
+ "a": From("test2").Select("id"),
+ })
+ deleteSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+}
+
+func (dds *deleteDatasetSuite) TestWhere_chainToSQL() {
+ t := dds.T()
+ ds1 := Delete("test").Where(
+ C("x").Eq(0),
+ C("y").Eq(1),
+ )
+
+ ds2 := ds1.Where(
+ C("z").Eq(2),
+ )
+
+ a := ds2.Where(
+ C("a").Eq("A"),
+ )
+ b := ds2.Where(
+ C("b").Eq("B"),
+ )
+ deleteSQL, _, err := a.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("a" = 'A'))`)
+ deleteSQL, _, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" `+
+ `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("b" = 'B'))`)
+}
+
+func (dds *deleteDatasetSuite) TestWhere_emptyToSQL() {
+ t := dds.T()
+ ds1 := Delete("test")
+
+ b := ds1.Where()
+ deleteSQL, _, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test"`)
+}
+
+func (dds *deleteDatasetSuite) TestClearWhere() {
+ t := dds.T()
+ w := Ex{
+ "a": 1,
+ }
+ ds := Delete("test").Where(w)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearWhere()
+ assert.Equal(t, ec, ds.ClearWhere().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestClearWhere_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("test")
+
+ b := ds1.Where(
+ C("a").Eq(1),
+ ).ClearWhere()
+ deleteSQL, _, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test"`)
+}
+
+func (dds *deleteDatasetSuite) TestOrder() {
+ t := dds.T()
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ o := C("a").Desc()
+ ec := dsc.SetOrder(o)
+ assert.Equal(t, ec, ds.Order(o).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+func (dds *deleteDatasetSuite) TestOrder_ToSQL() {
+ t := dds.T()
+
+ ds1 := Delete("test").WithDialect("order-on-delete")
+
+ b := ds1.Order(C("a").Asc(), L(`("a" + "b" > 2)`).Asc())
+ deleteSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
+}
+
+func (dds *deleteDatasetSuite) TestOrderAppend() {
+ t := dds.T()
+ ds := Delete("test").Order(C("a").Desc())
+ dsc := ds.GetClauses()
+ o := C("b").Desc()
+ ec := dsc.OrderAppend(o)
+ assert.Equal(t, ec, ds.OrderAppend(o).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestOrderAppend_ToSQL() {
+ t := dds.T()
+ ds := Delete("test").WithDialect("order-on-delete")
+ b := ds.Order(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
+ deleteSQL, _, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ b = ds.OrderAppend(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
+ deleteSQL, _, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+}
+
+func (dds *deleteDatasetSuite) TestClearOrder() {
+ t := dds.T()
+ ds := Delete("test").Order(C("a").Desc())
+ dsc := ds.GetClauses()
+ ec := dsc.ClearOrder()
+ assert.Equal(t, ec, ds.ClearOrder().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestClearOrder_ToSQL() {
+ t := dds.T()
+ ds := Delete("test").WithDialect("order-on-delete")
+ b := ds.Order(C("a").Asc().NullsFirst()).ClearOrder()
+ deleteSQL, _, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test"`)
+}
+
+func (dds *deleteDatasetSuite) TestLimit() {
+ t := dds.T()
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLimit(uint(1))
+ assert.Equal(t, ec, ds.Limit(1).GetClauses())
+ assert.Equal(t, dsc, ds.Limit(0).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestLimit_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("test").WithDialect("limit-on-delete")
+
+ b := ds1.Where(C("a").Gt(1)).Limit(10)
+ deleteSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > 1) LIMIT 10`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1), int64(10)}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > ?) LIMIT ?`)
+
+ b = ds1.Where(C("a").Gt(1)).Limit(0)
+ deleteSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > 1)`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > ?)`)
+}
+
+func (dds *deleteDatasetSuite) TestLimitAll() {
+ t := dds.T()
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLimit(L("ALL"))
+ assert.Equal(t, ec, ds.LimitAll().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestLimitAll_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("test").WithDialect("limit-on-delete")
+
+ b := ds1.Where(C("a").Gt(1)).LimitAll()
+
+ deleteSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > 1) LIMIT ALL`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > ?) LIMIT ALL`)
+
+ b = ds1.Where(C("a").Gt(1)).Limit(0).LimitAll()
+ deleteSQL, _, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > 1) LIMIT ALL`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > ?) LIMIT ALL`)
+}
+
+func (dds *deleteDatasetSuite) TestClearLimit() {
+ t := dds.T()
+ ds := Delete("test").Limit(1)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearLimit()
+ assert.Equal(t, ec, ds.ClearLimit().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestClearLimit_ToSQL() {
+ t := dds.T()
+ ds1 := Delete("test")
+
+ b := ds1.Where(C("a").Gt(1)).LimitAll().ClearLimit()
+ deleteSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > 1)`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > ?)`)
+
+ b = ds1.Where(C("a").Gt(1)).Limit(10).ClearLimit()
+ deleteSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > 1)`)
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" WHERE ("a" > ?)`)
+}
+
+func (dds *deleteDatasetSuite) TestReturning() {
+ t := dds.T()
+ ds := Delete("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetReturning(exp.NewColumnListExpression(C("a")))
+ assert.Equal(t, ec, ds.Returning("a").GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (dds *deleteDatasetSuite) TestReturning_ToSQL() {
+ t := dds.T()
+ ds := Delete("test")
+ b := ds.Returning("a")
+
+ deleteSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" RETURNING "a"`)
+
+ deleteSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, deleteSQL, `DELETE FROM "test" RETURNING "a"`)
+}
+
+func (dds *deleteDatasetSuite) TestToSQL() {
+ t := dds.T()
+ md := new(mocks.SQLDialect)
+ ds := Delete("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToDeleteSQL", sqlB, c).Return(nil).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (dds *deleteDatasetSuite) TestToSQL_Prepared() {
+ t := dds.T()
+ md := new(mocks.SQLDialect)
+ ds := Delete("test").Prepared(true).SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(true)
+ md.On("ToDeleteSQL", sqlB, c).Return(nil).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (dds *deleteDatasetSuite) TestToSQL_WithError() {
+ t := dds.T()
+ md := new(mocks.SQLDialect)
+ ds := Delete("test").SetDialect(md)
+ c := ds.GetClauses()
+ ee := errors.New("expected error")
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToDeleteSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(ee)
+ }).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Equal(t, ee, err)
+ md.AssertExpectations(t)
+}
+
+func (dds *deleteDatasetSuite) TestExecutor() {
+ t := dds.T()
+ mDb, _, err := sqlmock.New()
+ assert.NoError(t, err)
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDeleteDataset("mock", qf).From("items").Where(Ex{"id": Op{"gt": 10}})
+
+ dsql, args, err := ds.Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `DELETE FROM "items" WHERE ("id" > 10)`, dsql)
+
+ dsql, args, err = ds.Prepared(true).Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(10)}, args)
+ assert.Equal(t, `DELETE FROM "items" WHERE ("id" > ?)`, dsql)
+}
+
+func TestDeleteDataset(t *testing.T) {
+ suite.Run(t, new(deleteDatasetSuite))
+}
diff --git a/dialect/mysql/mysql.go b/dialect/mysql/mysql.go
index fd65d037..c0f97f3e 100644
--- a/dialect/mysql/mysql.go
+++ b/dialect/mysql/mysql.go
@@ -1,8 +1,8 @@
package mysql
import (
- "github.com/doug-martin/goqu/v7"
- "github.com/doug-martin/goqu/v7/exp"
+ "github.com/doug-martin/goqu/v8"
+ "github.com/doug-martin/goqu/v8/exp"
)
func DialectOptions() *goqu.SQLDialectOptions {
diff --git a/dialect/mysql/mysql_dialect_test.go b/dialect/mysql/mysql_dialect_test.go
index 944d7dc5..bac424c0 100644
--- a/dialect/mysql/mysql_dialect_test.go
+++ b/dialect/mysql/mysql_dialect_test.go
@@ -4,7 +4,7 @@ import (
"regexp"
"testing"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
@@ -13,7 +13,7 @@ type mysqlDialectSuite struct {
suite.Suite
}
-func (mds *mysqlDialectSuite) GetDs(table string) *goqu.Dataset {
+func (mds *mysqlDialectSuite) GetDs(table string) *goqu.SelectDataset {
return goqu.Dialect("mysql").From(table)
}
diff --git a/dialect/mysql/mysql_test.go b/dialect/mysql/mysql_test.go
index f4565226..9086453c 100644
--- a/dialect/mysql/mysql_test.go
+++ b/dialect/mysql/mysql_test.go
@@ -7,8 +7,8 @@ import (
"testing"
"time"
- "github.com/doug-martin/goqu/v7"
- _ "github.com/doug-martin/goqu/v7/dialect/mysql"
+ "github.com/doug-martin/goqu/v8"
+ _ "github.com/doug-martin/goqu/v8/dialect/mysql"
_ "github.com/go-sql-driver/mysql"
"github.com/stretchr/testify/assert"
@@ -224,7 +224,7 @@ func (mt *mysqlTest) TestInsert() {
ds := mt.db.From("entry")
now := time.Now()
e := entry{Int: 10, Float: 1.000000, String: "1.000000", Time: now, Bool: true, Bytes: []byte("1.000000")}
- _, err := ds.Insert(e).Exec()
+ _, err := ds.Insert().Rows(e).Executor().Exec()
assert.NoError(t, err)
var insertedEntry entry
@@ -239,19 +239,19 @@ func (mt *mysqlTest) TestInsert() {
{Int: 13, Float: 1.300000, String: "1.300000", Time: now, Bool: false, Bytes: []byte("1.300000")},
{Int: 14, Float: 1.400000, String: "1.400000", Time: now, Bool: true, Bytes: []byte("1.400000")},
}
- _, err = ds.Insert(entries).Exec()
+ _, err = ds.Insert().Rows(entries).Executor().Exec()
assert.NoError(t, err)
var newEntries []entry
assert.NoError(t, ds.Where(goqu.C("int").In([]uint32{11, 12, 13, 14})).ScanStructs(&newEntries))
assert.Len(t, newEntries, 4)
- _, err = ds.Insert(
+ _, err = ds.Insert().Rows(
entry{Int: 15, Float: 1.500000, String: "1.500000", Time: now, Bool: false, Bytes: []byte("1.500000")},
entry{Int: 16, Float: 1.600000, String: "1.600000", Time: now, Bool: true, Bytes: []byte("1.600000")},
entry{Int: 17, Float: 1.700000, String: "1.700000", Time: now, Bool: false, Bytes: []byte("1.700000")},
entry{Int: 18, Float: 1.800000, String: "1.800000", Time: now, Bool: true, Bytes: []byte("1.800000")},
- ).Exec()
+ ).Executor().Exec()
assert.NoError(t, err)
newEntries = newEntries[0:0]
@@ -264,7 +264,7 @@ func (mt *mysqlTest) TestInsertReturning() {
ds := mt.db.From("entry")
now := time.Now()
e := entry{Int: 10, Float: 1.000000, String: "1.000000", Time: now, Bool: true, Bytes: []byte("1.000000")}
- _, err := ds.Returning(goqu.Star()).Insert(e).ScanStruct(&e)
+ _, err := ds.Insert().Rows(e).Returning(goqu.Star()).Executor().ScanStruct(&e)
assert.Error(t, err)
}
@@ -277,7 +277,7 @@ func (mt *mysqlTest) TestUpdate() {
assert.NoError(t, err)
assert.True(t, found)
e.Int = 11
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Update(e).Exec()
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Update().Set(e).Executor().Exec()
assert.NoError(t, err)
count, err := ds.Where(goqu.C("int").Eq(11)).Count()
@@ -289,7 +289,11 @@ func (mt *mysqlTest) TestUpdateReturning() {
t := mt.T()
ds := mt.db.From("entry")
var id uint32
- _, err := ds.Where(goqu.C("int").Eq(11)).Returning("id").Update(goqu.Record{"int": 9}).ScanVal(&id)
+ _, err := ds.Where(goqu.C("int").Eq(11)).
+ Update().
+ Set(goqu.Record{"int": 9}).
+ Returning("id").
+ Executor().ScanVal(&id)
assert.Error(t, err)
assert.Equal(t, err.Error(), "goqu: adapter does not support RETURNING clause")
}
@@ -301,7 +305,7 @@ func (mt *mysqlTest) TestDelete() {
found, err := ds.Where(goqu.C("int").Eq(9)).Select("id").ScanStruct(&e)
assert.NoError(t, err)
assert.True(t, found)
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Exec()
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Executor().Exec()
assert.NoError(t, err)
count, err := ds.Count()
@@ -320,7 +324,7 @@ func (mt *mysqlTest) TestDelete() {
assert.NotEqual(t, e.ID, 0)
id = 0
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Returning("id").Delete().ScanVal(&id)
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Returning("id").Executor().ScanVal(&id)
assert.Equal(t, err.Error(), "goqu: adapter does not support RETURNING clause")
}
@@ -335,7 +339,7 @@ func (mt *mysqlTest) TestInsertIgnore() {
{Int: 9, Float: 7.200000, String: "7.200000", Time: now, Bytes: []byte("7.200000")},
{Int: 10, Float: 7.200000, String: "7.200000", Time: now, Bytes: []byte("7.200000")},
}
- _, err := ds.InsertIgnore(entries).Exec()
+ _, err := ds.Insert().Rows(entries).OnConflict(goqu.DoNothing()).Executor().Exec()
assert.NoError(t, err)
count, err := ds.Count()
@@ -343,25 +347,28 @@ func (mt *mysqlTest) TestInsertIgnore() {
assert.Equal(t, count, int64(11))
}
-func (mt *mysqlTest) TestInsertConflict() {
+func (mt *mysqlTest) TestInsert_OnConflict() {
t := mt.T()
ds := mt.db.From("entry")
now := time.Now()
// insert
e := entry{Int: 10, Float: 1.100000, String: "1.100000", Time: now, Bool: false, Bytes: []byte("1.100000")}
- _, err := ds.InsertConflict(goqu.DoNothing(), e).Exec()
+ _, err := ds.Insert().Rows(e).OnConflict(goqu.DoNothing()).Executor().Exec()
assert.NoError(t, err)
// duplicate
e = entry{Int: 10, Float: 2.100000, String: "2.100000", Time: now.Add(time.Hour * 100), Bool: false, Bytes: []byte("2.100000")}
- _, err = ds.InsertConflict(goqu.DoNothing(), e).Exec()
+ _, err = ds.Insert().Rows(e).OnConflict(goqu.DoNothing()).Executor().Exec()
assert.NoError(t, err)
// update
var entryActual entry
e2 := entry{Int: 10, String: "2.000000"}
- _, err = ds.InsertConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"}), e2).Exec()
+ _, err = ds.Insert().
+ Rows(e2).
+ OnConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"})).
+ Executor().Exec()
assert.NoError(t, err)
_, err = ds.Where(goqu.C("int").Eq(10)).ScanStruct(&entryActual)
assert.NoError(t, err)
@@ -372,7 +379,10 @@ func (mt *mysqlTest) TestInsertConflict() {
{Int: 8, Float: 6.100000, String: "6.100000", Time: now, Bytes: []byte("6.100000")},
{Int: 9, Float: 7.200000, String: "7.200000", Time: now, Bytes: []byte("7.200000")},
}
- _, err = ds.InsertConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"}).Where(goqu.C("int").Eq(9)), entries).Exec()
+ _, err = ds.Insert().
+ Rows(entries).
+ OnConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"}).Where(goqu.C("int").Eq(9))).
+ Executor().Exec()
assert.Equal(t, err.Error(), "goqu: adapter does not support upsert with where clause")
}
diff --git a/dialect/postgres/postgres.go b/dialect/postgres/postgres.go
index 006139dc..7431ad90 100644
--- a/dialect/postgres/postgres.go
+++ b/dialect/postgres/postgres.go
@@ -1,7 +1,7 @@
package postgres
import (
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
)
func DialectOptions() *goqu.SQLDialectOptions {
diff --git a/dialect/postgres/postgres_test.go b/dialect/postgres/postgres_test.go
index 56c9f0d6..9612f620 100644
--- a/dialect/postgres/postgres_test.go
+++ b/dialect/postgres/postgres_test.go
@@ -7,7 +7,7 @@ import (
"testing"
"time"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
"github.com/lib/pq"
"github.com/stretchr/testify/assert"
@@ -218,7 +218,7 @@ func (pt *postgresTest) TestInsert() {
ds := pt.db.From("entry")
now := time.Now()
e := entry{Int: 10, Float: 1.000000, String: "1.000000", Time: now, Bool: true, Bytes: []byte("1.000000")}
- _, err := ds.Insert(e).Exec()
+ _, err := ds.Insert().Rows(e).Executor().Exec()
assert.NoError(t, err)
var insertedEntry entry
@@ -233,19 +233,19 @@ func (pt *postgresTest) TestInsert() {
{Int: 13, Float: 1.300000, String: "1.300000", Time: now, Bool: false, Bytes: []byte("1.300000")},
{Int: 14, Float: 1.400000, String: "1.400000", Time: now, Bool: true, Bytes: []byte("1.400000")},
}
- _, err = ds.Insert(entries).Exec()
+ _, err = ds.Insert().Rows(entries).Executor().Exec()
assert.NoError(t, err)
var newEntries []entry
assert.NoError(t, ds.Where(goqu.C("int").In([]uint32{11, 12, 13, 14})).ScanStructs(&newEntries))
assert.Len(t, newEntries, 4)
- _, err = ds.Insert(
+ _, err = ds.Insert().Rows(
entry{Int: 15, Float: 1.500000, String: "1.500000", Time: now, Bool: false, Bytes: []byte("1.500000")},
entry{Int: 16, Float: 1.600000, String: "1.600000", Time: now, Bool: true, Bytes: []byte("1.600000")},
entry{Int: 17, Float: 1.700000, String: "1.700000", Time: now, Bool: false, Bytes: []byte("1.700000")},
entry{Int: 18, Float: 1.800000, String: "1.800000", Time: now, Bool: true, Bytes: []byte("1.800000")},
- ).Exec()
+ ).Executor().Exec()
assert.NoError(t, err)
newEntries = newEntries[0:0]
@@ -258,30 +258,30 @@ func (pt *postgresTest) TestInsertReturning() {
ds := pt.db.From("entry")
now := time.Now()
e := entry{Int: 10, Float: 1.000000, String: "1.000000", Time: now, Bool: true, Bytes: []byte("1.000000")}
- found, err := ds.Returning(goqu.Star()).Insert(e).ScanStruct(&e)
+ found, err := ds.Insert().Rows(e).Returning(goqu.Star()).Executor().ScanStruct(&e)
assert.NoError(t, err)
assert.True(t, found)
assert.True(t, e.ID > 0)
var ids []uint32
- assert.NoError(t, ds.Returning("id").Insert([]entry{
+ assert.NoError(t, ds.Insert().Rows([]entry{
{Int: 11, Float: 1.100000, String: "1.100000", Time: now, Bool: false, Bytes: []byte("1.100000")},
{Int: 12, Float: 1.200000, String: "1.200000", Time: now, Bool: true, Bytes: []byte("1.200000")},
{Int: 13, Float: 1.300000, String: "1.300000", Time: now, Bool: false, Bytes: []byte("1.300000")},
{Int: 14, Float: 1.400000, String: "1.400000", Time: now, Bool: true, Bytes: []byte("1.400000")},
- }).ScanVals(&ids))
+ }).Returning("id").Executor().ScanVals(&ids))
assert.Len(t, ids, 4)
for _, id := range ids {
assert.True(t, id > 0)
}
var ints []int64
- assert.NoError(t, ds.Returning("int").Insert(
+ assert.NoError(t, ds.Insert().Rows(
entry{Int: 15, Float: 1.500000, String: "1.500000", Time: now, Bool: false, Bytes: []byte("1.500000")},
entry{Int: 16, Float: 1.600000, String: "1.600000", Time: now, Bool: true, Bytes: []byte("1.600000")},
entry{Int: 17, Float: 1.700000, String: "1.700000", Time: now, Bool: false, Bytes: []byte("1.700000")},
entry{Int: 18, Float: 1.800000, String: "1.800000", Time: now, Bool: true, Bytes: []byte("1.800000")},
- ).ScanVals(&ints))
+ ).Returning("int").Executor().ScanVals(&ints))
assert.True(t, found)
assert.Equal(t, ints, []int64{15, 16, 17, 18})
}
@@ -294,7 +294,7 @@ func (pt *postgresTest) TestUpdate() {
assert.NoError(t, err)
assert.True(t, found)
e.Int = 11
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Update(e).Exec()
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Update().Set(e).Executor().Exec()
assert.NoError(t, err)
count, err := ds.Where(goqu.C("int").Eq(11)).Count()
@@ -302,7 +302,10 @@ func (pt *postgresTest) TestUpdate() {
assert.Equal(t, count, int64(1))
var id uint32
- found, err = ds.Where(goqu.C("int").Eq(11)).Returning("id").Update(goqu.Record{"int": 9}).ScanVal(&id)
+ found, err = ds.Where(goqu.C("int").Eq(11)).
+ Update().
+ Set(goqu.Record{"int": 9}).
+ Returning("id").Executor().ScanVal(&id)
assert.NoError(t, err)
assert.True(t, found)
assert.Equal(t, id, e.ID)
@@ -315,7 +318,7 @@ func (pt *postgresTest) TestDelete() {
found, err := ds.Where(goqu.C("int").Eq(9)).Select("id").ScanStruct(&e)
assert.NoError(t, err)
assert.True(t, found)
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Exec()
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Executor().Exec()
assert.NoError(t, err)
count, err := ds.Count()
@@ -334,49 +337,33 @@ func (pt *postgresTest) TestDelete() {
assert.NotEqual(t, e.ID, int64(0))
id = 0
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Returning("id").Delete().ScanVal(&id)
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Returning("id").Executor().ScanVal(&id)
assert.NoError(t, err)
assert.Equal(t, id, e.ID)
}
-func (pt *postgresTest) TestInsertIgnore() {
- t := pt.T()
- ds := pt.db.From("entry")
- now := time.Now()
-
- // insert one
- entries := []entry{
- {Int: 8, Float: 6.100000, String: "6.100000", Time: now, Bytes: []byte("6.100000")},
- {Int: 9, Float: 7.200000, String: "7.200000", Time: now, Bytes: []byte("7.200000")},
- {Int: 10, Float: 7.200000, String: "7.200000", Time: now, Bytes: []byte("7.200000")},
- }
- _, err := ds.InsertIgnore(entries).Exec()
- assert.NoError(t, err)
-
- count, err := ds.Count()
- assert.NoError(t, err)
- assert.Equal(t, count, int64(11))
-}
-
-func (pt *postgresTest) TestInsertConflict() {
+func (pt *postgresTest) TestInsert_OnConflict() {
t := pt.T()
ds := pt.db.From("entry")
now := time.Now()
// DO NOTHING insert
e := entry{Int: 10, Float: 1.100000, String: "1.100000", Time: now, Bool: false, Bytes: []byte("1.100000")}
- _, err := ds.InsertConflict(goqu.DoNothing(), e).Exec()
+ _, err := ds.Insert().Rows(e).OnConflict(goqu.DoNothing()).Executor().Exec()
assert.NoError(t, err)
// DO NOTHING duplicate
e = entry{Int: 10, Float: 2.100000, String: "2.100000", Time: now.Add(time.Hour * 100), Bool: false, Bytes: []byte("2.100000")}
- _, err = ds.InsertConflict(goqu.DoNothing(), e).Exec()
+ _, err = ds.Insert().Rows(e).OnConflict(goqu.DoNothing()).Executor().Exec()
assert.NoError(t, err)
// DO NOTHING update
var entryActual entry
e2 := entry{Int: 0, String: "2.000000"}
- _, err = ds.InsertConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"}), e2).Exec()
+ _, err = ds.Insert().
+ Rows(e2).
+ OnConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"})).
+ Executor().Exec()
assert.NoError(t, err)
_, err = ds.Where(goqu.C("int").Eq(0)).ScanStruct(&entryActual)
assert.NoError(t, err)
@@ -387,7 +374,11 @@ func (pt *postgresTest) TestInsertConflict() {
{Int: 1, Float: 6.100000, String: "6.100000", Time: now, Bytes: []byte("6.100000")},
{Int: 2, Float: 7.200000, String: "7.200000", Time: now, Bytes: []byte("7.200000")},
}
- _, err = ds.InsertConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"}).Where(goqu.I("excluded.int").Eq(2)), entries).Exec()
+ _, err = ds.Insert().
+ Rows(entries).
+ OnConflict(goqu.DoUpdate("int", goqu.Record{"string": "upsert"}).Where(goqu.I("excluded.int").Eq(2))).
+ Executor().
+ Exec()
assert.NoError(t, err)
var entry8, entry9 entry
diff --git a/dialect/sqlite3/sqlite3.go b/dialect/sqlite3/sqlite3.go
index 59083774..cb81dfe8 100644
--- a/dialect/sqlite3/sqlite3.go
+++ b/dialect/sqlite3/sqlite3.go
@@ -1,8 +1,8 @@
package sqlite3
import (
- "github.com/doug-martin/goqu/v7"
- "github.com/doug-martin/goqu/v7/exp"
+ "github.com/doug-martin/goqu/v8"
+ "github.com/doug-martin/goqu/v8/exp"
)
func DialectOptions() *goqu.SQLDialectOptions {
diff --git a/dialect/sqlite3/sqlite3_dialect_test.go b/dialect/sqlite3/sqlite3_dialect_test.go
index 2622f6f6..91d8fe31 100644
--- a/dialect/sqlite3/sqlite3_dialect_test.go
+++ b/dialect/sqlite3/sqlite3_dialect_test.go
@@ -4,7 +4,7 @@ import (
"regexp"
"testing"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
@@ -14,7 +14,7 @@ type sqlite3DialectSuite struct {
suite.Suite
}
-func (sds *sqlite3DialectSuite) GetDs(table string) *goqu.Dataset {
+func (sds *sqlite3DialectSuite) GetDs(table string) *goqu.SelectDataset {
return goqu.Dialect("sqlite3").From(table)
}
diff --git a/dialect/sqlite3/sqlite3_test.go b/dialect/sqlite3/sqlite3_test.go
index b9f7cbf7..6976f9e0 100644
--- a/dialect/sqlite3/sqlite3_test.go
+++ b/dialect/sqlite3/sqlite3_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"time"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
@@ -237,7 +237,7 @@ func (st *sqlite3Suite) TestInsert() {
ds := st.db.From("entry")
now := time.Now()
e := entry{Int: 10, Float: 1.000000, String: "1.000000", Time: now, Bool: true, Bytes: []byte("1.000000")}
- _, err := ds.Insert(e).Exec()
+ _, err := ds.Insert().Rows(e).Executor().Exec()
assert.NoError(t, err)
var insertedEntry entry
@@ -252,19 +252,19 @@ func (st *sqlite3Suite) TestInsert() {
{Int: 13, Float: 1.300000, String: "1.300000", Time: now, Bool: false, Bytes: []byte("1.300000")},
{Int: 14, Float: 1.400000, String: "1.400000", Time: now, Bool: true, Bytes: []byte("1.400000")},
}
- _, err = ds.Insert(entries).Exec()
+ _, err = ds.Insert().Rows(entries).Executor().Exec()
assert.NoError(t, err)
var newEntries []entry
assert.NoError(t, ds.Where(goqu.C("int").In([]uint32{11, 12, 13, 14})).ScanStructs(&newEntries))
assert.Len(t, newEntries, 4)
- _, err = ds.Insert(
+ _, err = ds.Insert().Rows(
entry{Int: 15, Float: 1.500000, String: "1.500000", Time: now, Bool: false, Bytes: []byte("1.500000")},
entry{Int: 16, Float: 1.600000, String: "1.600000", Time: now, Bool: true, Bytes: []byte("1.600000")},
entry{Int: 17, Float: 1.700000, String: "1.700000", Time: now, Bool: false, Bytes: []byte("1.700000")},
entry{Int: 18, Float: 1.800000, String: "1.800000", Time: now, Bool: true, Bytes: []byte("1.800000")},
- ).Exec()
+ ).Executor().Exec()
assert.NoError(t, err)
newEntries = newEntries[0:0]
@@ -272,12 +272,12 @@ func (st *sqlite3Suite) TestInsert() {
assert.Len(t, newEntries, 4)
}
-func (st *sqlite3Suite) TestInsertReturning() {
+func (st *sqlite3Suite) TestInsert_returning() {
t := st.T()
ds := st.db.From("entry")
now := time.Now()
e := entry{Int: 10, Float: 1.000000, String: "1.000000", Time: now, Bool: true, Bytes: []byte("1.000000")}
- _, err := ds.Returning(goqu.Star()).Insert(e).ScanStruct(&e)
+ _, err := ds.Insert().Rows(e).Returning(goqu.Star()).Executor().ScanStruct(&e)
assert.Error(t, err)
}
@@ -290,7 +290,7 @@ func (st *sqlite3Suite) TestUpdate() {
assert.NoError(t, err)
assert.True(t, found)
e.Int = 11
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Update(e).Exec()
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Update().Set(e).Executor().Exec()
assert.NoError(t, err)
count, err := ds.Where(goqu.C("int").Eq(11)).Count()
@@ -302,7 +302,12 @@ func (st *sqlite3Suite) TestUpdateReturning() {
t := st.T()
ds := st.db.From("entry")
var id uint32
- _, err := ds.Where(goqu.C("int").Eq(11)).Returning("id").Update(map[string]interface{}{"int": 9}).ScanVal(&id)
+ _, err := ds.
+ Where(goqu.C("int").Eq(11)).
+ Update().
+ Set(map[string]interface{}{"int": 9}).
+ Returning("id").
+ Executor().ScanVal(&id)
assert.Error(t, err)
assert.Equal(t, err.Error(), "goqu: adapter does not support RETURNING clause")
}
@@ -314,7 +319,7 @@ func (st *sqlite3Suite) TestDelete() {
found, err := ds.Where(goqu.C("int").Eq(9)).Select("id").ScanStruct(&e)
assert.NoError(t, err)
assert.True(t, found)
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Exec()
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Executor().Exec()
assert.NoError(t, err)
count, err := ds.Count()
@@ -333,7 +338,7 @@ func (st *sqlite3Suite) TestDelete() {
assert.NotEqual(t, e.ID, int64(0))
id = 0
- _, err = ds.Where(goqu.C("id").Eq(e.ID)).Returning("id").Delete().ScanVal(&id)
+ _, err = ds.Where(goqu.C("id").Eq(e.ID)).Delete().Returning("id").Executor().ScanVal(&id)
assert.Equal(t, err.Error(), "goqu: adapter does not support RETURNING clause")
}
diff --git a/docs/database.md b/docs/database.md
new file mode 100644
index 00000000..5f2f4ca0
--- /dev/null
+++ b/docs/database.md
@@ -0,0 +1,77 @@
+
+### Database
+
+The Database also allows you to execute queries but expects raw SQL to execute. The supported methods are
+
+* [`Exec`](http://godoc.org/github.com/doug-martin/goqu#Database.Exec)
+* [`Prepare`](http://godoc.org/github.com/doug-martin/goqu#Database.Prepare)
+* [`Query`](http://godoc.org/github.com/doug-martin/goqu#Database.Query)
+* [`QueryRow`](http://godoc.org/github.com/doug-martin/goqu#Database.QueryRow)
+* [`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanStructs)
+* [`ScanStruct`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanStruct)
+* [`ScanVals`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanVals)
+* [`ScanVal`](http://godoc.org/github.com/doug-martin/goqu#Database.ScanVal)
+* [`Begin`](http://godoc.org/github.com/doug-martin/goqu#Database.Begin)
+
+
+### Transactions
+
+`goqu` has builtin support for transactions to make the use of the Datasets and querying seamless
+
+```go
+tx, err := db.Begin()
+if err != nil{
+ return err
+}
+//use tx.From to get a dataset that will execute within this transaction
+update := tx.From("user").
+ Where(goqu.Ex("password": nil}).
+ Update(goqu.Record{"status": "inactive"})
+if _, err = update.Exec(); err != nil{
+ if rErr := tx.Rollback(); rErr != nil{
+ return rErr
+ }
+ return err
+}
+if err = tx.Commit(); err != nil{
+ return err
+}
+return
+```
+
+The [`TxDatabase`](http://godoc.org/github.com/doug-martin/goqu/#TxDatabase) also has all methods that the [`Database`](http://godoc.org/github.com/doug-martin/goqu/#Database) has along with
+
+* [`Commit`](http://godoc.org/github.com/doug-martin/goqu#TxDatabase.Commit)
+* [`Rollback`](http://godoc.org/github.com/doug-martin/goqu#TxDatabase.Rollback)
+* [`Wrap`](http://godoc.org/github.com/doug-martin/goqu#TxDatabase.Wrap)
+
+#### Wrap
+
+The [`TxDatabase.Wrap`](http://godoc.org/github.com/doug-martin/goqu/#TxDatabase.Wrap) is a convience method for automatically handling `COMMIT` and `ROLLBACK`
+
+```go
+tx, err := db.Begin()
+if err != nil{
+ return err
+}
+err = tx.Wrap(func() error{
+ update := tx.From("user").
+ Where(goqu.Ex("password": nil}).
+ Update(goqu.Record{"status": "inactive"})
+ return update.Exec()
+})
+//err will be the original error from the update statement, unless there was an error executing ROLLBACK
+if err != nil{
+ return err
+}
+```
+
+
+## Logging
+
+To enable trace logging of SQL statements use the [`Database.Logger`](http://godoc.org/github.com/doug-martin/goqu/#Database.Logger) method to set your logger.
+
+**NOTE** The logger must implement the [`Logger`](http://godoc.org/github.com/doug-martin/goqu/#Logger) interface
+
+**NOTE** If you start a transaction using a database your set a logger on the transaction will inherit that logger automatically
+
diff --git a/docs/deleting.md b/docs/deleting.md
new file mode 100644
index 00000000..c342fd25
--- /dev/null
+++ b/docs/deleting.md
@@ -0,0 +1,268 @@
+# Deleting
+
+* [Creating A DeleteDataset](#create)
+* Examples
+ * [Delete All](#delete-all)
+ * [Prepared](#prepared)
+ * [Where](#where)
+ * [Order](#order)
+ * [Limit](#limit)
+ * [Returning](#returning)
+ * [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)**
+
+When you just want to create some quick SQL, this mostly follows the `Postgres` with the exception of placeholders for prepared statements.
+
+```go
+sql, _, _ := goqu.Delete("table").ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+DELETE FROM "table"
+```
+
+**[`SelectDataset.Delete`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Delete)**
+
+If you already have a `SelectDataset` you can invoke `Delete()` to get a `DeleteDataset`
+
+**NOTE** This method will also copy over the `WITH`, `WHERE`, `ORDER`, and `LIMIT` from the `SelectDataset`
+
+```go
+
+ds := goqu.From("table")
+
+sql, _, _ := ds.Delete().ToSQL()
+fmt.Println(sql)
+
+sql, _, _ = ds.Where(goqu.C("foo").Eq("bar")).Delete().ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+DELETE FROM "table"
+DELETE FROM "table" WHERE "foo"='bar'
+```
+
+**[`DialectWrapper.Delete`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Delete)**
+
+Use this when you want to create SQL for a specific `dialect`
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+dialect := goqu.Dialect("mysql")
+
+sql, _, _ := dialect.Delete("table").ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+DELETE FROM `table`
+```
+
+**[`Database.Delete`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Delete)**
+
+Use this when you want to execute the SQL or create SQL for the drivers dialect.
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+mysqlDB := //initialize your db
+db := goqu.New("mysql", mysqlDB)
+
+sql, _, _ := db.Delete("table").ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+DELETE FROM `table`
+```
+
+### Examples
+
+For more examples visit the **[Docs](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset)**
+
+
+**Delete All Records**
+
+```go
+ds := goqu.Delete("items")
+
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+DELETE FROM "items" []
+```
+
+
+**[`Prepared`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset.Prepared)**
+
+```go
+sql, _, _ := goqu.Delete("test").Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+DELETE FROM "test" WHERE (("a" > ?) AND ("b" < ?) AND ("c" IS NULL) AND ("d" IN (?, ?, ?))) [10 10 a b c]
+```
+
+
+**[`Where`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset.Where)**
+
+```go
+sql, _, _ := goqu.Delete("test").Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+DELETE FROM "test" WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+```
+
+
+**[`Order`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset.Order)**
+
+**NOTE** This will only work if your dialect supports it
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+ds := goqu.Dialect("mysql").Delete("test").Order(goqu.C("a").Asc())
+sql, _, _ := ds.ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+DELETE FROM `test` ORDER BY `a` ASC
+```
+
+
+**[`Limit`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset.Limit)**
+
+**NOTE** This will only work if your dialect supports it
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+ds := goqu.Dialect("mysql").Delete("test").Limit(10)
+sql, _, _ := ds.ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+DELETE FROM `test` LIMIT 10
+```
+
+
+**[`Returning`](https://godoc.org/github.com/doug-martin/goqu/#DeleteDataset.Returning)**
+
+Returning a single column example.
+
+```go
+ds := goqu.Delete("items")
+sql, args, _ := ds.Returning("id").ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+DELETE FROM "items" RETURNING "id" []
+```
+
+Returning multiple columns
+
+```go
+sql, _, _ := goqu.Delete("test").Returning("a", "b").ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+DELETE FROM "items" RETURNING "a", "b"
+```
+
+Returning all columns
+
+```go
+sql, _, _ := goqu.Delete("test").Returning(goqu.T("test").All()).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+DELETE FROM "test" RETURNING "test".*
+```
+
+## Executing Deletes
+
+To execute DELETES use [`Database.Delete`](https://godoc.org/github.com/doug-martin/goqu/#Database.Delete) to create your dataset
+
+### Examples
+
+
+**Executing a Delete**
+```go
+db := getDb()
+
+de := db.Delete("goqu_user").
+ Where(goqu.Ex{"first_name": "Bob"}).
+ Executor()
+
+if r, err := de.Exec(); err != nil {
+ fmt.Println(err.Error())
+} else {
+ c, _ := r.RowsAffected()
+ fmt.Printf("Deleted %d users", c)
+}
+```
+
+Output:
+
+```
+Deleted 1 users
+```
+
+If you use the RETURNING clause you can scan into structs or values.
+
+```go
+db := getDb()
+
+de := db.Delete("goqu_user").
+ Where(goqu.C("last_name").Eq("Yukon")).
+ Returning(goqu.C("id")).
+ Executor()
+
+var ids []int64
+if err := de.ScanVals(&ids); err != nil {
+ fmt.Println(err.Error())
+} else {
+ fmt.Printf("Deleted users [ids:=%+v]", ids)
+}
+```
+
+Output:
+
+```
+Deleted users [ids:=[1 2 3]]
+```
\ No newline at end of file
diff --git a/docs/dialect.md b/docs/dialect.md
new file mode 100644
index 00000000..5f4ff1d3
--- /dev/null
+++ b/docs/dialect.md
@@ -0,0 +1,167 @@
+# Dialect
+
+Dialects allow goqu the build the correct SQL for each database. There are three dialects that come packaged with `goqu`
+
+* [mysql](./dialect/mysql/mysql.go) - `import _ "github.com/doug-martin/goqu/v8/dialect/mysql"`
+* [postgres](./dialect/postgres/postgres.go) - `import _ "github.com/doug-martin/goqu/v8/dialect/postgres"`
+* [sqlite3](./dialect/sqlite3/sqlite3.go) - `import _ "github.com/doug-martin/goqu/v8/dialect/sqlite3"`
+
+**NOTE** Dialects work like drivers in go where they are not registered until you import the package.
+
+Below are examples for each dialect. Notice how the dialect is imported and then looked up using `goqu.Dialect`
+
+
+### Postgres
+```go
+import (
+ "fmt"
+ "github.com/doug-martin/goqu/v8"
+ // import the dialect
+ _ "github.com/doug-martin/goqu/v8/dialect/postgres"
+)
+
+// look up the dialect
+dialect := goqu.Dialect("postgres")
+
+// use dialect.From to get a dataset to build your SQL
+ds := dialect.From("test").Where(goqu.Ex{"id": 10})
+sql, args, err := ds.ToSQL()
+if err != nil{
+ fmt.Println("An error occurred while generating the SQL", err.Error())
+}else{
+ fmt.Println(sql, args)
+}
+```
+
+Output:
+```
+SELECT * FROM "test" WHERE "id" = 10 []
+```
+
+
+### MySQL
+```go
+import (
+ "fmt"
+ "github.com/doug-martin/goqu/v8"
+ // import the dialect
+ _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+)
+
+// look up the dialect
+dialect := goqu.Dialect("mysql")
+
+// use dialect.From to get a dataset to build your SQL
+ds := dialect.From("test").Where(goqu.Ex{"id": 10})
+sql, args, err := ds.ToSQL()
+if err != nil{
+ fmt.Println("An error occurred while generating the SQL", err.Error())
+}else{
+ fmt.Println(sql, args)
+}
+```
+
+Output:
+```
+SELECT * FROM `test` WHERE `id` = 10 []
+```
+
+
+### SQLite3
+```go
+import (
+ "fmt"
+ "github.com/doug-martin/goqu/v8"
+ // import the dialect
+ _ "github.com/doug-martin/goqu/v8/dialect/sqlite3"
+)
+
+// look up the dialect
+dialect := goqu.Dialect("sqlite3")
+
+// use dialect.From to get a dataset to build your SQL
+ds := dialect.From("test").Where(goqu.Ex{"id": 10})
+sql, args, err := ds.ToSQL()
+if err != nil{
+ fmt.Println("An error occurred while generating the SQL", err.Error())
+}else{
+ fmt.Println(sql, args)
+}
+```
+
+Output:
+```
+SELECT * FROM `test` WHERE `id` = 10 []
+```
+
+### Executing Queries
+
+You can also create a `goqu.Database` instance to query records.
+
+In the example below notice that we imported the dialect and driver for side effect only.
+
+```go
+import (
+ "database/sql"
+ "github.com/doug-martin/goqu/v8"
+ _ "github.com/doug-martin/goqu/v8/dialect/postgres"
+ _ "github.com/lib/pq"
+)
+
+dialect := goqu.Dialect("postgres")
+
+pgDb, err := sql.Open("postgres", "user=postgres dbname=goqupostgres sslmode=disable ")
+if err != nil {
+ panic(err.Error())
+}
+db := dialect.DB(pgDb)
+
+// "SELECT COUNT(*) FROM "user";
+if count, err := db.From("user").Count(); err != nil {
+ fmt.Println(err.Error())
+}else{
+ fmt.Printf("User count = %d", count)
+}
+```
+
+
+## Custom Dialects
+
+Dialects in goqu are the foundation of building the correct SQL for each DB dialect.
+
+### Dialect Options
+
+Most SQL dialects share a majority of their syntax, for this reason `goqu` has a [default set of dialect options]((http://godoc.org/github.com/doug-martin/goqu/#DefaultDialectOptions)) that can be used as a base for any new Dialect.
+
+When creating a new `SQLDialect` you just need to override the default values that are documented in [`SQLDialectOptions`](http://godoc.org/github.com/doug-martin/goqu/#SQLDialectOptions).
+
+Take a look at [`postgres`](./dialect/postgres/postgres.go), [`mysql`](./dialect/mysql/mysql.go) and [`sqlite3`](./dialect/sqlite3/sqlite3.go) for examples.
+
+### Creating a custom dialect
+
+When creating a new dialect you must register it using [`RegisterDialect`](http://godoc.org/github.com/doug-martin/goqu/#RegisterDialect). This method requires 2 arguments.
+
+1. `dialect string` - The name of your dialect
+2. `opts SQLDialectOptions` - The custom options for your dialect
+
+For example you could create a custom dialect that replaced the default quote `'"'` with a backtick `
+```go
+opts := goqu.DefaultDialectOptions()
+opts.QuoteRune = '`'
+goqu.RegisterDialect("custom-dialect", opts)
+
+dialect := goqu.Dialect("custom-dialect")
+
+ds := dialect.From("test")
+
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+SELECT * FROM `test` []
+```
+
+For more examples look at [`postgres`](./dialect/postgres/postgres.go), [`mysql`](./dialect/mysql/mysql.go) and [`sqlite3`](./dialect/sqlite3/sqlite3.go) for examples.
+
diff --git a/docs/expressions.md b/docs/expressions.md
new file mode 100644
index 00000000..a06682f6
--- /dev/null
+++ b/docs/expressions.md
@@ -0,0 +1,390 @@
+# Expressions
+
+`goqu` provides an idiomatic DSL for generating SQL. Datasets only act as a clause builder (i.e. Where, From, Select), most of these clause methods accept Expressions which are the building blocks for your SQL statement, you can think of them as fragments of SQL.
+
+* [`Ex{}`](#ex) - A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause.
+* [`ExOr{}`](#ex-or)- OR version of `Ex`. A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause
+* [`S`](#S) - An Identifier that represents a schema. With a schema identifier you can fully qualify tables and columns.
+* [`T`](#T) - An Identifier that represents a Table. With a Table identifier you can fully qualify columns.
+* [`C`](#C) - An Identifier that represents a Column. See the docs for more examples
+* [`I`](#I) - An Identifier represents a schema, table, or column or any combination. I parses identifiers seperated by a . character.
+* [`L`](#L) - An SQL literal.
+* [`And`](#and) - AND multiple expressions together.
+* [`Or`](#or) - OR multiple expressions together.
+* [Complex Example] - Complex Example using most of the Expression DSL.
+
+The entry points for expressions are:
+
+
+**[`Ex{}`](https://godoc.org/github.com/doug-martin/goqu#Ex)**
+
+A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause. By default `Ex` will use the equality operator except in cases where the equality operator will not work, see the example below.
+
+```go
+sql, _, _ := db.From("items").Where(goqu.Ex{
+ "col1": "a",
+ "col2": 1,
+ "col3": true,
+ "col4": false,
+ "col5": nil,
+ "col6": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```sql
+SELECT * FROM "items" WHERE (("col1" = 'a') AND ("col2" = 1) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IS NULL) AND ("col6" IN ('a', 'b', 'c')))
+```
+
+You can also use the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) map which allows you to create more complex expressions using the map syntax. When using the `Op` map the key is the name of the comparison you want to make (e.g. `"neq"`, `"like"`, `"is"`, `"in"`), the key is case insensitive.
+
+```go
+sql, _, _ := db.From("items").Where(goqu.Ex{
+ "col1": goqu.Op{"neq": "a"},
+ "col3": goqu.Op{"isNot": true},
+ "col6": goqu.Op{"notIn": []string{"a", "b", "c"}},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```sql
+SELECT * FROM "items" WHERE (("col1" != 'a') AND ("col3" IS NOT TRUE) AND ("col6" NOT IN ('a', 'b', 'c')))
+```
+For a more complete examples see the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) and [`Ex`](https://godoc.org/github.com/doug-martin/goqu#Ex) docs
+
+
+**[`ExOr{}`](https://godoc.org/github.com/doug-martin/goqu#ExOr)**
+
+A map where the key will become an Identifier and the Key is the value, this is most commonly used in the Where clause. By default `ExOr` will use the equality operator except in cases where the equality operator will not work, see the example below.
+
+```go
+sql, _, _ := db.From("items").Where(goqu.ExOr{
+ "col1": "a",
+ "col2": 1,
+ "col3": true,
+ "col4": false,
+ "col5": nil,
+ "col6": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```sql
+SELECT * FROM "items" WHERE (("col1" = 'a') OR ("col2" = 1) OR ("col3" IS TRUE) OR ("col4" IS FALSE) OR ("col5" IS NULL) OR ("col6" IN ('a', 'b', 'c')))
+```
+
+You can also use the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) map which allows you to create more complex expressions using the map syntax. When using the `Op` map the key is the name of the comparison you want to make (e.g. `"neq"`, `"like"`, `"is"`, `"in"`), the key is case insensitive.
+
+```go
+sql, _, _ := db.From("items").Where(goqu.ExOr{
+ "col1": goqu.Op{"neq": "a"},
+ "col3": goqu.Op{"isNot": true},
+ "col6": goqu.Op{"notIn": []string{"a", "b", "c"}},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```sql
+SELECT * FROM "items" WHERE (("col1" != 'a') OR ("col3" IS NOT TRUE) OR ("col6" NOT IN ('a', 'b', 'c')))
+```
+For a more complete examples see the [`Op`](https://godoc.org/github.com/doug-martin/goqu#Op) and [`ExOr`](https://godoc.org/github.com/doug-martin/goqu#Ex) docs
+
+
+**[`S()`](https://godoc.org/github.com/doug-martin/goqu#S)**
+
+An Identifier that represents a schema. With a schema identifier you can fully qualify tables and columns.
+
+```go
+s := goqu.S("my_schema")
+
+// "my_schema"."my_table"
+t := s.Table("my_table")
+
+// "my_schema"."my_table"."my_column"
+
+sql, _, _ := goqu.From(t).Select(t.Col("my_column").ToSQL()
+// SELECT "my_schema"."my_table"."my_column" FROM "my_schema"."my_table"
+fmt.Println(sql)
+```
+
+
+**[`T()`](https://godoc.org/github.com/doug-martin/goqu#T)**
+
+An Identifier that represents a Table. With a Table identifier you can fully qualify columns.
+```go
+t := s.Table("my_table")
+
+sql, _, _ := goqu.From(t).Select(t.Col("my_column").ToSQL()
+// SELECT "my_table"."my_column" FROM "my_table"
+fmt.Println(sql)
+
+// qualify the table with a schema
+sql, _, _ := goqu.From(t.Schema("my_schema")).Select(t.Col("my_column").ToSQL()
+// SELECT "my_table"."my_column" FROM "my_schema"."my_table"
+fmt.Println(sql)
+```
+
+
+**[`C()`](https://godoc.org/github.com/doug-martin/goqu#C)**
+
+An Identifier that represents a Column. See the [docs]((https://godoc.org/github.com/doug-martin/goqu#C)) for more examples
+
+```go
+sql, _, _ := goqu.From("table").Where(goqu.C("col").Eq(10)).ToSQL()
+// SELECT * FROM "table" WHERE "col" = 10
+fmt.Println(sql)
+```
+
+
+**[`I()`](https://godoc.org/github.com/doug-martin/goqu#I)**
+
+An Identifier represents a schema, table, or column or any combination. `I` parses identifiers seperated by a `.` character.
+
+```go
+// with three parts it is assumed you have provided a schema, table and column
+goqu.I("my_schema.table.col") == goqu.S("my_schema").Table("table").Col("col")
+
+// with two parts it is assumed you have provided a table and column
+goqu.I("table.col") == goqu.T("table").Col("col")
+
+// with a single value it is the same as calling goqu.C
+goqu.I("col") == goqu.C("col")
+```
+
+
+**[`L()`](https://godoc.org/github.com/doug-martin/goqu#L)**
+
+An SQL literal. You may find yourself in a situation where an IdentifierExpression cannot expression an SQL fragment that your database supports. In that case you can use a LiteralExpression
+
+```go
+// manual casting
+goqu.L(`"json"::TEXT = "other_json"::text`)
+
+// custom function invocation
+goqu.L(`custom_func("a")`)
+
+// postgres JSON access
+goqu.L(`"json_col"->>'someField'`).As("some_field")
+```
+
+You can also use placeholders in your literal with a `?` character. `goqu` will handle changing it to what the dialect needs (e.g. `?` mysql, `$1` postgres, `?` sqlite3).
+
+**NOTE** If your query is not prepared the placeholders will be properly interpolated.
+
+```go
+goqu.L("col IN (?, ?, ?)", "a", "b", "c")
+```
+
+Putting it together
+
+```go
+ds := db.From("test").Where(
+ goqu.L(`("json"::TEXT = "other_json"::TEXT)`),
+ goqu.L("col IN (?, ?, ?)", "a", "b", "c"),
+)
+
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ := ds.Prepared(true).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```sql
+SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ('a', 'b', 'c') []
+-- assuming postgres dialect
+SELECT * FROM "test" WHERE ("json"::TEXT = "other_json"::TEXT) AND col IN ($1, $2, $3) [a, b, c]
+```
+
+
+**[`And()`](https://godoc.org/github.com/doug-martin/goqu#And)**
+
+You can use the `And` function to AND multiple expressions together.
+
+**NOTE** By default goqu will AND expressions together
+
+```go
+ds := goqu.From("test").Where(
+ goqu.And(
+ goqu.C("col").Gt(10),
+ goqu.C("col").Lt(20),
+ ),
+)
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = ds.Prepared(true).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```sql
+SELECT * FROM "test" WHERE (("col" > 10) AND ("col" < 20)) []
+SELECT * FROM "test" WHERE (("col" > ?) AND ("col" < ?)) [10 20]
+```
+
+
+**[`Or()`](https://godoc.org/github.com/doug-martin/goqu#Or)**
+
+You can use the `Or` function to OR multiple expressions together.
+
+```go
+ds := goqu.From("test").Where(
+ goqu.Or(
+ goqu.C("col").Eq(10),
+ goqu.C("col").Eq(20),
+ ),
+)
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = ds.Prepared(true).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```sql
+SELECT * FROM "test" WHERE (("col" = 10) OR ("col" = 20)) []
+SELECT * FROM "test" WHERE (("col" = ?) OR ("col" = ?)) [10 20]
+```
+
+You can also use `Or` and `And` functions in tandem which will give you control not only over how the Expressions are joined together, but also how they are grouped
+
+```go
+ds := goqu.From("items").Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Eq(100),
+ goqu.C("c").Neq("test"),
+ ),
+ ),
+)
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = ds.Prepared(true).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```sql
+SELECT * FROM "items" WHERE (("a" > 10) OR (("b" = 100) AND ("c" != 'test'))) []
+SELECT * FROM "items" WHERE (("a" > ?) OR (("b" = ?) AND ("c" != ?))) [10 100 test]
+```
+
+You can also use Or with the map syntax
+```go
+ds := goqu.From("test").Where(
+ goqu.Or(
+ // Ex will be anded together
+ goqu.Ex{
+ "col1": 1,
+ "col2": true,
+ },
+ goqu.Ex{
+ "col3": nil,
+ "col4": "foo",
+ },
+ ),
+)
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = ds.Prepared(true).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```sql
+SELECT * FROM "test" WHERE ((("col1" = 1) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = 'foo'))) []
+SELECT * FROM "test" WHERE ((("col1" = ?) AND ("col2" IS TRUE)) OR (("col3" IS NULL) AND ("col4" = ?))) [1 foo]
+```
+
+
+## Complex Example
+
+This example uses most of the features of the `goqu` Expression DSL
+
+```go
+ds := db.From("test").
+ Select(goqu.COUNT("*")).
+ InnerJoin(goqu.I("test2"), goqu.On(goqu.Ex{"test.fkey": goqu.I("test2.id")})).
+ LeftJoin(goqu.I("test3"), goqu.On(goqu.Ex{"test2.fkey": goqu.I("test3.id")})).
+ Where(
+ goqu.Ex{
+ "test.name": goqu.Op{"like": regexp.MustCompile("^(a|b)")},
+ "test2.amount": goqu.Op{"isNot": nil},
+ },
+ goqu.ExOr{
+ "test3.id": nil,
+ "test3.status": []string{"passed", "active", "registered"},
+ },
+ ).
+ Order(goqu.I("test.created").Desc().NullsLast()).
+ GroupBy(goqu.I("test.user_id")).
+ Having(goqu.AVG("test3.age").Gt(10))
+
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql)
+
+sql, args, _ := ds.Prepared(true).ToSQL()
+fmt.Println(sql)
+```
+
+Using the Expression syntax
+```go
+ds := db.From("test").
+ Select(goqu.COUNT("*")).
+ InnerJoin(goqu.I("test2"), goqu.On(goqu.I("test.fkey").Eq(goqu.I("test2.id")))).
+ LeftJoin(goqu.I("test3"), goqu.On(goqu.I("test2.fkey").Eq(goqu.I("test3.id")))).
+ Where(
+ goqu.I("test.name").Like(regexp.MustCompile("^(a|b)")),
+ goqu.I("test2.amount").IsNotNull(),
+ goqu.Or(
+ goqu.I("test3.id").IsNull(),
+ goqu.I("test3.status").In("passed", "active", "registered"),
+ ),
+ ).
+ Order(goqu.I("test.created").Desc().NullsLast()).
+ GroupBy(goqu.I("test.user_id")).
+ Having(goqu.AVG("test3.age").Gt(10))
+
+sql, args, _ := ds.ToSQL()
+fmt.Println(sql)
+
+sql, args, _ := ds.Prepared(true).ToSQL()
+fmt.Println(sql)
+```
+
+Both examples generate the following SQL
+
+```sql
+-- interpolated
+SELECT COUNT(*)
+FROM "test"
+ INNER JOIN "test2" ON ("test"."fkey" = "test2"."id")
+ LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id")
+WHERE ((("test"."name" ~ '^(a|b)') AND ("test2"."amount" IS NOT NULL)) AND
+ (("test3"."id" IS NULL) OR ("test3"."status" IN ('passed', 'active', 'registered'))))
+GROUP BY "test"."user_id"
+HAVING (AVG("test3"."age") > 10)
+ORDER BY "test"."created" DESC NULLS LAST []
+
+-- prepared
+SELECT COUNT(*)
+FROM "test"
+ INNER JOIN "test2" ON ("test"."fkey" = "test2"."id")
+ LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id")
+WHERE ((("test"."name" ~ ?) AND ("test2"."amount" IS NOT NULL)) AND
+ (("test3"."id" IS NULL) OR ("test3"."status" IN (?, ?, ?))))
+GROUP BY "test"."user_id"
+HAVING (AVG("test3"."age") > ?)
+ORDER BY "test"."created" DESC NULLS LAST [^(a|b) passed active registered 10]
+```
+
diff --git a/docs/inserting.md b/docs/inserting.md
new file mode 100644
index 00000000..0aabd3b1
--- /dev/null
+++ b/docs/inserting.md
@@ -0,0 +1,327 @@
+# Inserting
+
+* [Creating An InsertDataset](#create)
+* Examples
+ * [Insert Cols and Vals](#insert-cols-vals)
+ * [Insert `goqu.Record`](#insert-record)
+ * [Insert Structs](#insert-structs)
+ * [Insert Map](#insert-map)
+ * [Insert From Query](#insert-from-query)
+ * [Returning](#returning)
+ * [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)**
+
+When you just want to create some quick SQL, this mostly follows the `Postgres` with the exception of placeholders for prepared statements.
+
+```go
+ds := goqu.Insert("user").Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+insertSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley')
+```
+
+**[`SelectDataset.Insert`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Insert)**
+
+If you already have a `SelectDataset` you can invoke `Insert()` to get a `InsertDataset`
+
+**NOTE** This method will also copy over the `WITH` clause as well as the `FROM`
+
+```go
+ds := goqu.From("user")
+
+ds := ds.Insert().Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+insertSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley')
+```
+
+**[`DialectWrapper.Insert`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Insert)**
+
+Use this when you want to create SQL for a specific `dialect`
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+dialect := goqu.Dialect("mysql")
+
+ds := dialect.Insert().Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+insertSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+INSERT INTO `user` (`first_name`, `last_name`) VALUES ('Greg', 'Farley')
+```
+
+**[`Database.Insert`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Insert)**
+
+Use this when you want to execute the SQL or create SQL for the drivers dialect.
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+mysqlDB := //initialize your db
+db := goqu.New("mysql", mysqlDB)
+
+ds := db.Insert().Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+insertSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+INSERT INTO `user` (`first_name`, `last_name`) VALUES ('Greg', 'Farley')
+```
+
+### Examples
+
+For more examples visit the **[Docs](https://godoc.org/github.com/doug-martin/goqu/#InsertDataset)**
+
+
+**Insert with Cols and Vals**
+
+```go
+ds := goqu.Insert("user").
+ Cols("first_name", "last_name").
+ Vals(
+ goqu.Vals{"Greg", "Farley"},
+ goqu.Vals{"Jimmy", "Stewart"},
+ goqu.Vals{"Jeff", "Jeffers"},
+ )
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+
+Output:
+```sql
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+```
+
+
+**Insert `goqu.Record`**
+
+```go
+ds := goqu.Insert("user").Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+ goqu.Record{"first_name": "Jimmy", "last_name": "Stewart"},
+ goqu.Record{"first_name": "Jeff", "last_name": "Jeffers"},
+)
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+```
+
+
+**Insert Structs**
+
+```go
+type User struct {
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+}
+ds := goqu.Insert("user").Rows(
+ User{FirstName: "Greg", LastName: "Farley"},
+ User{FirstName: "Jimmy", LastName: "Stewart"},
+ User{FirstName: "Jeff", LastName: "Jeffers"},
+)
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+```
+
+
+**Insert `map[string]interface{}`**
+
+```go
+ds := goqu.Insert("user").Rows(
+ map[string]interface{}{"first_name": "Greg", "last_name": "Farley"},
+ map[string]interface{}{"first_name": "Jimmy", "last_name": "Stewart"},
+ map[string]interface{}{"first_name": "Jeff", "last_name": "Jeffers"},
+)
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+```
+
+
+**Insert from query**
+
+```go
+ds := goqu.Insert("user").Prepared(true).
+ FromQuery(goqu.From("other_table"))
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+
+Output:
+```
+INSERT INTO "user" SELECT * FROM "other_table" []
+```
+
+You can also specify the columns
+
+```go
+ds := goqu.Insert("user").Prepared(true).
+ Cols("first_name", "last_name").
+ FromQuery(goqu.From("other_table").Select("fn", "ln"))
+insertSQL, args, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+
+Output:
+```
+INSERT INTO "user" ("first_name", "last_name") SELECT "fn", "ln" FROM "other_table" []
+```
+
+
+**Returning Clause**
+
+Returning a single column example.
+
+```go
+sql, _, _ := goqu.Insert("test").
+ Rows(goqu.Record{"a": "a", "b": "b"}).
+ Returning("id").
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "id"
+```
+
+Returning multiple columns
+
+```go
+sql, _, _ = goqu.Insert("test").
+ Rows(goqu.Record{"a": "a", "b": "b"}).
+ Returning("a", "b").
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "a", "b"
+```
+
+Returning all columns
+
+```go
+sql, _, _ = goqu.Insert("test").
+ Rows(goqu.Record{"a": "a", "b": "b"}).
+ Returning(goqu.T("test").All()).
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "test".*
+```
+
+
+## Executing Inserts
+
+To execute INSERTS use [`Database.Insert`](https://godoc.org/github.com/doug-martin/goqu/#Database.Insert) to create your dataset
+
+### Examples
+
+**Executing an single Insert**
+```go
+db := getDb()
+
+insert := db.Insert("goqu_user").Rows(
+ goqu.Record{"first_name": "Jed", "last_name": "Riley", "created": time.Now()},
+).Executor()
+
+if _, err := insert.Exec(); err != nil {
+ fmt.Println(err.Error())
+} else {
+ fmt.Println("Inserted 1 user")
+}
+```
+
+Output:
+
+```
+Inserted 1 user
+```
+
+**Executing multiple inserts**
+
+```go
+db := getDb()
+
+users := []goqu.Record{
+ {"first_name": "Greg", "last_name": "Farley", "created": time.Now()},
+ {"first_name": "Jimmy", "last_name": "Stewart", "created": time.Now()},
+ {"first_name": "Jeff", "last_name": "Jeffers", "created": time.Now()},
+}
+
+insert := db.Insert("goqu_user").Rows(users).Executor()
+if _, err := insert.Exec(); err != nil {
+ fmt.Println(err.Error())
+} else {
+ fmt.Printf("Inserted %d users", len(users))
+}
+
+```
+
+Output:
+```
+Inserted 3 users
+```
+
+If you use the RETURNING clause you can scan into structs or values.
+
+```go
+db := getDb()
+
+insert := db.Insert("goqu_user").Returning(goqu.C("id")).Rows(
+ goqu.Record{"first_name": "Jed", "last_name": "Riley", "created": time.Now()},
+).Executor()
+
+var id int64
+if _, err := insert.ScanVal(&id); err != nil {
+ fmt.Println(err.Error())
+} else {
+ fmt.Printf("Inserted 1 user id:=%d\n", id)
+}
+```
+
+Output:
+
+```
+Inserted 1 user id:=5
+```
\ No newline at end of file
diff --git a/docs/interpolation.md b/docs/interpolation.md
new file mode 100644
index 00000000..774334b9
--- /dev/null
+++ b/docs/interpolation.md
@@ -0,0 +1,56 @@
+# Prepared Statements
+
+By default the `goqu` will interpolate all parameters, if you do not want to have values interpolated you can use the [`Prepared`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.Prepared) method to prevent this.
+
+**Note** For the examples all placeholders are `?` this will be dialect specific when using other examples (e.g. Postgres `$1, $2...`)
+
+```go
+
+preparedDs := db.From("items").Prepared(true)
+
+sql, args, _ := preparedDs.Where(goqu.Ex{
+ "col1": "a",
+ "col2": 1,
+ "col3": true,
+ "col4": false,
+ "col5": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = preparedDs.Insert().Rows(
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = preparedDs.Update().Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
+
+sql, args, _ = preparedDs.
+ Delete().
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ ToSQL()
+fmt.Println(sql, args)
+
+// Output:
+// SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IN (?, ?, ?))) [a 1 a b c]
+// INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
+// UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
+// DELETE FROM "items" WHERE ("id" > ?) [10]
+```
+
+When setting prepared to true executing the SQL using the different querying methods will also use the non-interpolated SQL also.
+
+```go
+var items []Item
+sql, args, _ := db.From("items").Prepared(true).Where(goqu.Ex{
+ "col1": "a",
+ "col2": 1,
+}).ScanStructs(&items)
+
+//Is the same as
+db.ScanStructs(&items, `SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?))`, "a", 1)
+```
+
diff --git a/docs/selecting.md b/docs/selecting.md
new file mode 100644
index 00000000..e7fafde6
--- /dev/null
+++ b/docs/selecting.md
@@ -0,0 +1,715 @@
+# Selecting
+
+* [Creating a SelectDataset](#create)
+* Building SQL
+ * [`Select`](#select)
+ * [`SelectDistinct`](#select_distinct)
+ * [`From`](#from)
+ * [`Join`](#joins)
+ * [`Where`](#where)
+ * [`Limit`](#limit)
+ * [`Offset`](#offset)
+ * [`GroupBy`](#group_by)
+ * [`Having`](#having)
+* 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
+ * [`ScanVals`](#scan-vals)- Scans a rows of 1 column into a slice of primitive values
+ * [`ScanVal`](#scan-val) - Scans a row of 1 column into a primitive value, returns false if a row wasnt found.
+ * [`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)**
+
+When you just want to create some quick SQL, this mostly follows the `Postgres` with the exception of placeholders for prepared statements.
+
+```go
+sql, _, _ := goqu.From("table").ToSQL()
+fmt.Println(sql)
+
+sql, _, _ := goqu.Select(goqu.L("NOW()")).ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+SELECT * FROM "table"
+SELECT NOW()
+```
+
+**[`DialectWrapper.From`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.From) and [`DialectWrapper.Select`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Select)**
+
+Use this when you want to create SQL for a specific `dialect`
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+dialect := goqu.Dialect("mysql")
+
+sql, _, _ := dialect.From("table").ToSQL()
+fmt.Println(sql)
+
+sql, _, _ := dialect.Select(goqu.L("NOW()")).ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+SELECT * FROM `table`
+SELECT NOW()
+```
+
+**[`Database.From`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.From) and [`Database.Select`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.From)**
+
+Use this when you want to execute the SQL or create SQL for the drivers dialect.
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+mysqlDB := //initialize your db
+db := goqu.New("mysql", mysqlDB)
+
+sql, _, _ := db.From("table").ToSQL()
+fmt.Println(sql)
+
+sql, _, _ := db.Select(goqu.L("NOW()")).ToSQL()
+fmt.Println(sql)
+```
+Output:
+```
+SELECT * FROM `table`
+SELECT NOW()
+```
+
+### Examples
+
+For more examples visit the **[Docs](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset)**
+
+
+**[`Select`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Select)**
+
+```go
+sql, _, _ := goqu.From("test").Select("a", "b", "c").ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```sql
+SELECT "a", "b", "c" FROM "test"
+```
+
+You can also ues another dataset in your select
+
+```go
+ds := goqu.From("test")
+fromDs := ds.Select("age").Where(goqu.C("age").Gt(10))
+sql, _, _ := ds.From().Select(fromDs).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT (SELECT "age" FROM "test" WHERE ("age" > 10))
+```
+
+Selecting a literal
+
+```go
+sql, _, _ := goqu.From("test").Select(goqu.L("a + b").As("sum")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT a + b AS "sum" FROM "test"
+```
+
+Select aggregate functions
+
+```go
+sql, _, _ := goqu.From("test").Select(
+ goqu.COUNT("*").As("age_count"),
+ goqu.MAX("age").As("max_age"),
+ goqu.AVG("age").As("avg_age"),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT COUNT(*) AS "age_count", MAX("age") AS "max_age", AVG("age") AS "avg_age" FROM "test"
+```
+
+Selecting columns from a struct
+
+```go
+ds := goqu.From("test")
+
+type myStruct struct {
+ Name string
+ Address string `db:"address"`
+ EmailAddress string `db:"email_address"`
+}
+
+// Pass with pointer
+sql, _, _ := ds.Select(&myStruct{}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT "address", "email_address", "name" FROM "test"
+```
+
+
+**[`SelectDistinct`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.SelectDistinct)**
+
+```go
+sql, _, _ := goqu.From("test").SelectDistinct("a", "b").ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT DISTINCT "a", "b" FROM "test"
+```
+
+
+**[`From`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.From)**
+
+Overriding the original from
+```go
+ds := goqu.From("test")
+sql, _, _ := ds.From("test2").ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test2"
+```
+
+From another dataset
+
+```go
+ds := goqu.From("test")
+fromDs := ds.Where(goqu.C("age").Gt(10))
+sql, _, _ := ds.From(fromDs).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM (SELECT * FROM "test" WHERE ("age" > 10)) AS "t1"
+```
+
+From an aliased dataset
+
+```go
+ds := goqu.From("test")
+fromDs := ds.Where(goqu.C("age").Gt(10))
+sql, _, _ := ds.From(fromDs.As("test2")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM (SELECT * FROM "test" WHERE ("age" > 10)) AS "test2"
+```
+
+
+**[`Join`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Join)**
+
+```go
+sql, _, _ := goqu.From("test").Join(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{"test.fkey": goqu.I("test2.Id")}),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" INNER JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`InnerJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.InnerJoin)
+
+```go
+sql, _, _ := goqu.From("test").InnerJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{"test.fkey": goqu.I("test2.Id")}),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" INNER JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`FullOuterJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.FullOuterJoin)
+
+```go
+sql, _, _ := goqu.From("test").FullOuterJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{
+ "test.fkey": goqu.I("test2.Id"),
+ }),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" FULL OUTER JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`RightOuterJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.RightOuterJoin)
+
+```go
+sql, _, _ := goqu.From("test").RightOuterJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{
+ "test.fkey": goqu.I("test2.Id"),
+ }),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" RIGHT OUTER JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`LeftOuterJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.LeftOuterJoin)
+
+```go
+sql, _, _ := goqu.From("test").LeftOuterJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{
+ "test.fkey": goqu.I("test2.Id"),
+ }),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" LEFT OUTER JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`FullJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.FullJoin)
+
+```go
+sql, _, _ := goqu.From("test").FullJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{
+ "test.fkey": goqu.I("test2.Id"),
+ }),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" FULL JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+
+[`RightJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.RightJoin)
+
+```go
+sql, _, _ := goqu.From("test").RightJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{
+ "test.fkey": goqu.I("test2.Id"),
+ }),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" RIGHT JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`LeftJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.LeftJoin)
+
+```go
+sql, _, _ := goqu.From("test").LeftJoin(
+ goqu.T("test2"),
+ goqu.On(goqu.Ex{
+ "test.fkey": goqu.I("test2.Id"),
+ }),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" LEFT JOIN "test2" ON ("test"."fkey" = "test2"."Id")
+```
+
+[`NaturalJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.NaturalJoin)
+
+```go
+sql, _, _ := goqu.From("test").NaturalJoin(goqu.T("test2")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" NATURAL JOIN "test2"
+```
+
+[`NaturalLeftJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.NaturalLeftJoin)
+
+```go
+sql, _, _ := goqu.From("test").NaturalLeftJoin(goqu.T("test2")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" NATURAL LEFT JOIN "test2"
+```
+
+[`NaturalRightJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.NaturalRightJoin)
+
+```go
+sql, _, _ := goqu.From("test").NaturalRightJoin(goqu.T("test2")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" NATURAL RIGHT LEFT JOIN "test2"
+```
+
+[`NaturalFullJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.NaturalFullJoin)
+
+```go
+sql, _, _ := goqu.From("test").NaturalFullJoin(goqu.T("test2")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" NATURAL FULL LEFT JOIN "test2"
+```
+
+[`CrossJoin`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.CrossJoin)
+
+```go
+sql, _, _ := goqu.From("test").CrossJoin(goqu.T("test2")).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" CROSS JOIN "test2"
+```
+
+
+**[`Where`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Where)**
+
+You can use `goqu.Ex` to create an ANDed condition
+```go
+sql, _, _ := goqu.From("test").Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT * FROM "test" WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+```
+
+You can use `goqu.ExOr` to create an ORed condition
+
+```go
+sql, _, _ := goqu.From("test").Where(goqu.ExOr{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+}).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" WHERE (("a" > 10) OR ("b" < 10) OR ("c" IS NULL) OR ("d" IN ('a', 'b', 'c')))
+```
+
+You can use `goqu.Ex` with `goqu.ExOr` for complex expressions
+
+```go
+// You can use Or with Ex to Or multiple Ex maps together
+sql, _, _ := goqu.From("test").Where(
+ goqu.Or(
+ goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ },
+ goqu.Ex{
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ },
+ ),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT * FROM "test" WHERE ((("a" > 10) AND ("b" < 10)) OR (("c" IS NULL) AND ("d" IN ('a', 'b', 'c'))))
+```
+
+You can also use identifiers to create your where condition
+
+```go
+sql, _, _ := goqu.From("test").Where(
+ goqu.C("a").Gt(10),
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ goqu.C("d").In("a", "b", "c"),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+SELECT * FROM "test" WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+```
+
+Using `goqu.Or` to create ORed expression
+
+```go
+// You can use a combination of Ors and Ands
+sql, _, _ := goqu.From("test").Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT * FROM "test" WHERE (("a" > 10) OR (("b" < 10) AND ("c" IS NULL)))
+```
+
+
+**[`Limit`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Limit)**
+
+```go
+ds := goqu.From("test").Limit(10)
+sql, _, _ := ds.ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT * FROM "test" LIMIT 10
+```
+
+
+**[`Offset`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Offset)**
+
+```go
+ds := goqu.From("test").Offset(2)
+sql, _, _ := ds.ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT * FROM "test" OFFSET 2
+```
+
+
+**[`GroupBy`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.GroupBy)**
+
+```go
+sql, _, _ := goqu.From("test").
+ Select(goqu.SUM("income").As("income_sum")).
+ GroupBy("age").
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT SUM("income") AS "income_sum" FROM "test" GROUP BY "age"
+```
+
+
+**[`Having`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Having)**
+
+```go
+sql, _, _ = goqu.From("test").GroupBy("age").Having(goqu.SUM("income").Gt(1000)).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+
+```
+SELECT * FROM "test" GROUP BY "age" HAVING (SUM("income") > 1000)
+```
+
+## 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)**
+
+Scans rows into a slice of structs
+
+**NOTE** [`ScanStructs`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanStructs) will only select the columns that can be scanned in to the structs unless you have explicitly selected certain columns.
+
+ ```go
+type User struct{
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+}
+
+var users []User
+//SELECT "first_name", "last_name" FROM "user";
+if err := db.From("user").ScanStructs(&users); err != nil{
+ panic(err.Error())
+}
+fmt.Printf("\n%+v", users)
+
+var users []User
+//SELECT "first_name" FROM "user";
+if err := db.From("user").Select("first_name").ScanStructs(&users); err != nil{
+ panic(err.Error())
+}
+fmt.Printf("\n%+v", users)
+```
+
+
+**[`ScanStruct`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanStruct)**
+
+Scans a row into a slice a struct, returns false if a row wasnt found
+
+**NOTE** [`ScanStruct`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanStruct) will only select the columns that can be scanned in to the struct unless you have explicitly selected certain columns.
+
+```go
+type User struct{
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+}
+
+var user User
+// SELECT "first_name", "last_name" FROM "user" LIMIT 1;
+found, err := db.From("user").ScanStruct(&user)
+if err != nil{
+ fmt.Println(err.Error())
+ return
+}
+if !found {
+ fmt.Println("No user found")
+} else {
+ fmt.Printf("\nFound user: %+v", user)
+}
+```
+
+
+**NOTE** Using the `goqu.SetColumnRenameFunction` function, you can change the function that's used to rename struct fields when struct tags aren't defined
+
+```go
+import "strings"
+
+goqu.SetColumnRenameFunction(strings.ToUpper)
+
+type User struct{
+ FirstName string
+ LastName string
+}
+
+var user User
+//SELECT "FIRSTNAME", "LASTNAME" FROM "user" LIMIT 1;
+found, err := db.From("user").ScanStruct(&user)
+// ...
+```
+
+
+**[`ScanVals`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.ScanVals)**
+
+Scans a rows of 1 column into a slice of primitive values
+
+```go
+var ids []int64
+if err := db.From("user").Select("id").ScanVals(&ids); err != nil{
+ fmt.Println(err.Error())
+ return
+}
+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.
+
+**Note** when using the dataset a `LIMIT` of 1 is automatically applied.
+```go
+var id int64
+found, err := db.From("user").Select("id").ScanVal(&id)
+if err != nil{
+ fmt.Println(err.Error())
+ return
+}
+if !found{
+ fmt.Println("No id found")
+}else{
+ fmt.Printf("\nFound id: %d", id)
+}
+```
+
+
+**[`Count`](http://godoc.org/github.com/doug-martin/goqu#SelectDataset.Count)**
+
+Returns the count for the current query
+
+```go
+count, err := db.From("user").Count()
+if err != nil{
+ fmt.Println(err.Error())
+ return
+}
+fmt.Printf("\nCount:= %d", count)
+```
+
+
+**[`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
+
+```go
+var ids []int64
+if err := db.From("user").Pluck(&ids, "id"); err != nil{
+ fmt.Println(err.Error())
+ return
+}
+fmt.Printf("\nIds := %+v", ids)
+```
\ No newline at end of file
diff --git a/docs/updating.md b/docs/updating.md
new file mode 100644
index 00000000..6b1474c4
--- /dev/null
+++ b/docs/updating.md
@@ -0,0 +1,333 @@
+# Updating
+
+* [Create a UpdateDataset](#create)
+* Examples
+ * [Set with `goqu.Record`](#set-record)
+ * [Set with struct](#set-struct)
+ * [Set with map](#set-map)
+ * [Where](#where)
+ * [Order](#order)
+ * [Limit](#limit)
+ * [Returning](#returning)
+ * [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)**
+
+When you just want to create some quick SQL, this mostly follows the `Postgres` with the exception of placeholders for prepared statements.
+
+```go
+ds := goqu.Update("user").Set(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+updateSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+UPDATE "user" SET "first_name"='Greg', "last_name"='Farley'
+```
+
+**[`SelectDataset.Update`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Update)**
+
+If you already have a `SelectDataset` you can invoke `Update()` to get a `UpdateDataset`
+
+**NOTE** This method will also copy over the `WITH`, `WHERE`, `ORDER`, and `LIMIT` clauses from the update
+
+```go
+ds := goqu.From("user")
+
+updateSQL, _, _ := ds.Update().Set(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+).ToSQL()
+fmt.Println(insertSQL, args)
+
+updateSQL, _, _ = ds.Where(goqu.C("first_name").Eq("Gregory")).Update().Set(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+).ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+UPDATE "user" SET "first_name"='Greg', "last_name"='Farley'
+UPDATE "user" SET "first_name"='Greg', "last_name"='Farley' WHERE "first_name"='Gregory'
+```
+
+**[`DialectWrapper.Update`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Update)**
+
+Use this when you want to create SQL for a specific `dialect`
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+dialect := goqu.Dialect("mysql")
+
+ds := dialect.Update("user").Set(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+updateSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+UPDATE `user` SET `first_name`='Greg', `last_name`='Farley'
+```
+
+**[`Database.Update`](https://godoc.org/github.com/doug-martin/goqu/#DialectWrapper.Update)**
+
+Use this when you want to execute the SQL or create SQL for the drivers dialect.
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+mysqlDB := //initialize your db
+db := goqu.New("mysql", mysqlDB)
+
+ds := db.Update("user").Set(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+)
+updateSQL, _, _ := ds.ToSQL()
+fmt.Println(insertSQL, args)
+```
+Output:
+```
+UPDATE `user` SET `first_name`='Greg', `last_name`='Farley'
+```
+
+### Examples
+
+For more examples visit the **[Docs](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset)**
+
+
+**[Set with `goqu.Record`](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Set)**
+
+```go
+sql, args, _ := goqu.Update("items").Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+```
+
+
+**[Set with Struct](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Set)**
+
+```go
+type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+}
+sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+```
+
+With structs you can also skip fields by using the `skipupdate` tag
+
+```go
+type item struct {
+ Address string `db:"address"`
+ Name string `db:"name" goqu:"skipupdate"`
+}
+sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+UPDATE "items" SET "address"='111 Test Addr' []
+```
+
+
+**[Set with Map](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Set)**
+
+```go
+sql, args, _ := goqu.Update("items").Set(
+ map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
+).ToSQL()
+fmt.Println(sql, args)
+```
+
+Output:
+```
+UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+```
+
+
+**[Where](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Where)**
+
+```go
+sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+UPDATE "test" SET "foo"='bar' WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+```
+
+
+**[Order](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Order)**
+
+**NOTE** This will only work if your dialect supports it
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Order(goqu.C("a").Asc())
+sql, _, _ := ds.ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+UPDATE `test` SET `foo`='bar' ORDER BY `a` ASC
+```
+
+
+**[Order](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Limit)**
+
+**NOTE** This will only work if your dialect supports it
+
+```go
+// import _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+
+ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Limit(10)
+sql, _, _ := ds.ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+UPDATE `test` SET `foo`='bar' LIMIT 10
+```
+
+
+**[Returning](https://godoc.org/github.com/doug-martin/goqu/#UpdateDataset.Returning)**
+
+Returning a single column example.
+
+```go
+sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Returning("id").
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+UPDATE "test" SET "foo"='bar' RETURNING "id"
+```
+
+Returning multiple columns
+
+```go
+sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Returning("a", "b").
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+UPDATE "test" SET "foo"='bar' RETURNING "a", "b"
+```
+
+Returning all columns
+
+```go
+sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Returning(goqu.T("test").All()).
+ ToSQL()
+fmt.Println(sql)
+```
+
+Output:
+```
+UPDATE "test" SET "foo"='bar' RETURNING "test".*
+```
+
+
+## Executing Updates
+
+To execute Updates use [`goqu.Database#Update`](https://godoc.org/github.com/doug-martin/goqu/#Database.Update) to create your dataset
+
+### Examples
+
+**Executing an update**
+```go
+db := getDb()
+
+update := db.Update("goqu_user").
+ Where(goqu.C("first_name").Eq("Bob")).
+ Set(goqu.Record{"first_name": "Bobby"}).
+ Executor()
+
+if r, err := update.Exec(); err != nil {
+ fmt.Println(err.Error())
+} else {
+ c, _ := r.RowsAffected()
+ fmt.Printf("Updated %d users", c)
+}
+```
+
+Output:
+
+```
+Updated 1 users
+```
+
+**Executing with Returning**
+
+```go
+db := getDb()
+
+update := db.Update("goqu_user").
+ Set(goqu.Record{"last_name": "ucon"}).
+ Where(goqu.Ex{"last_name": "Yukon"}).
+ Returning("id").
+ Executor()
+
+var ids []int64
+if err := update.ScanVals(&ids); err != nil {
+ fmt.Println(err.Error())
+} else {
+ fmt.Printf("Updated users with ids %+v", ids)
+}
+
+```
+
+Output:
+```
+Updated users with ids [1 2 3]
+```
\ No newline at end of file
diff --git a/docs/version_migration.md b/docs/version_migration.md
new file mode 100644
index 00000000..d2c908c8
--- /dev/null
+++ b/docs/version_migration.md
@@ -0,0 +1,212 @@
+## Migrating Between Versions
+
+* [To v8](#v8)
+* [To v7](#v7)
+
+
+### `v7 to v8`
+
+A major change the the API was made in `v8` to seperate concerns between the different SQL statement types.
+
+**Why the change?**
+
+1. There were feature requests that could not be cleanly implemented with everything in a single dataset.
+2. Too much functionality was encapsulated in a single datastructure.
+ * It was unclear what methods could be used for each SQL statement type.
+ * Changing a feature for one statement type had the possiblity of breaking another statement type.
+ * Test coverage was decent but was almost solely concerned about SELECT statements, breaking them up allowed for focused testing on each statement type.
+ * Most the SQL generation methods (`ToInsertSQL`, `ToUpdateSQL` etc.) took arguments which lead to an ugly API that was not uniform for each statement type, and proved to be inflexible.
+
+**What Changed**
+
+There are now five dataset types, `SelectDataset`, `InsertDataset`, `UpdateDataset`, `DeleteDataset` and `TruncateDataset`
+
+Each dataset type has its own entry point.
+
+* `goqu.From`, `Database#From`, `DialectWrapper#From` - Create SELECT
+* `goqu.Insert`, `Database#Insert`, `DialectWrapper#Insert` - Create INSERT
+* `goqu.Update`, `Database#db.Update`, `DialectWrapper#Update` - Create UPDATE
+* `goqu.Delete`, `Database#Delete`, `DialectWrapper#Delete` - Create DELETE
+* `goqu.Truncate`, `Database#Truncate`, `DialectWrapper#Truncate` - Create TRUNCATE
+
+`ToInsertSQL`, `ToUpdateSQL`, `ToDeleteSQL`, and `ToTruncateSQL` (and variations of them) methods have been removed from the `SelectDataset`. Instead use the `ToSQL` methods on each dataset type.
+
+Each dataset type will have an `Executor` and `ToSQL` method so a common interface can be created for each type.
+
+**How to insert.**
+
+In older versions of `goqu` there was `ToInsertSQL` method on the `Dataset` in the latest version there is a new entry point to create INSERTS.
+
+```go
+// old way
+goqu.From("test").ToInsertSQL(...rows)
+
+// new way
+goqu.Insert("test").Rows(...rows).ToSQL()
+goqu.From("test").Insert().Rows(...rows).ToSQL()
+```
+
+In older versions of `goqu` there was `Insert` method on the `Dataset` to execute an insert in the latest version there is a new `Exectutor` method.
+
+```go
+// old way
+db.From("test").Insert(...rows).Exec()
+
+// new way
+db.Insert("test").Rows(...rows).Executor().Exec()
+// or
+db.From("test").Insert().Rows(...rows).Executor().Exec()
+```
+
+The new `InsertDataset` also has an `OnConflict` method that replaces the `ToInsertConflictSQL` and `ToInsertIgnoreSQL`
+
+```go
+// old way
+goqu.From("test").ToInsertIgnoreSQL(...rows)
+
+// new way
+goqu.Insert("test").Rows(...rows).OnConflict(goqu.DoNothing()).ToSQL()
+// OR
+goqu.From("test").Insert().Rows(...rows).OnConflict(goqu.DoNothing()).ToSQL()
+```
+
+```go
+// old way
+goqu.From("items").ToInsertConflictSQL(
+ goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}),
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+)
+fmt.Println(sql, args)
+
+// new way
+goqu.Insert("test").
+ Rows(
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+ ).
+ OnConflict(goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")})).
+ ToSQL()
+// OR
+goqu.From("test").
+ Insert().
+ Rows(
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+ ).
+ OnConflict(goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")})).
+ ToSQL()
+```
+
+**How to update.**
+
+In older versions of `goqu` there was `ToUpdateSQL` method on the `Dataset` in the latest version there is a new entry point to create UPDATEs.
+
+```go
+// old way
+goqu.From("items").ToUpdateSQL(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+)
+
+// new way
+goqu.Update("items").
+ Set(goqu.Record{"name": "Test", "address": "111 Test Addr"}).
+ ToSQL()
+// OR
+goqu.From("items").
+ Update()
+ Set(goqu.Record{"name": "Test", "address": "111 Test Addr"}).
+ ToSQL()
+```
+
+In older versions of `goqu` there was `Insert` method on the `Dataset` to execute an insert in the latest version there is a new `Exectutor` method.
+
+```go
+// old way
+db.From("items").Update(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+).Exec()
+
+// new way
+db.Update("items").
+ Set(goqu.Record{"name": "Test", "address": "111 Test Addr"}).
+ Executor().Exec()
+// OR
+db.From("items").
+ Update().
+ Set(goqu.Record{"name": "Test", "address": "111 Test Addr"}).
+ Executor().Exec()
+
+```
+
+**How to delete.**
+
+In older versions of `goqu` there was `ToDeleteSQL` method on the `Dataset` in the latest version there is a new entry point to create DELETEs.
+
+```go
+// old way
+goqu.From("items").
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ ToDeleteSQL()
+
+// new way
+goqu.Delete("items").
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ ToSQL()
+// OR
+goqu.From("items").
+ Delete()
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ ToSQL()
+```
+
+In older versions of `goqu` there was `Delete` method on the `Dataset` to execute an insert in the latest version there is a new `Exectutor` method.
+
+```go
+// old way
+db.From("items").
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ Delete().Exec()
+
+// new way
+db.Delete("items").
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ Executor().Exec()
+// OR
+db.From("items").
+ Delete()
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ Executor().Exec()
+```
+
+**How to truncate.**
+
+In older versions of `goqu` there was `ToTruncateSQL` method on the `Dataset` in the latest version there is a new entry point to create TRUNCATEs.
+
+```go
+// old way
+goqu.From("items").ToTruncateSQL()
+
+// new way
+goqu.Truncate("items").ToSQL()
+```
+
+
+### ` `ToSQL`
+ * `ToInsertSql` -> `ToInsertSQL`
+ * `ToUpdateSql` -> `ToUpdateSQL`
+ * `ToDeleteSql` -> `ToDeleteSQL`
+ * `ToTruncateSql` -> `ToTruncateSQL`
+* Abstracted out `dialect_options` from the adapter to make the dialect self contained.
+ * This also removed the `dataset<->adapter` co dependency making the dialect self contained.
+ * Added new dialect options to specify the order than SQL statements are built.
+* Refactored the `goqu.I` method.
+ * Added new `goqu.S`, `goqu.T` and `goqu.C` methods to clarify why type of identifier you are using.
+ * `goqu.I` should only be used when you have a qualified identifier (e.g. `goqu.I("my_schema.my_table.my_col")
+* Added new `goqu.Dialect` method to make using `goqu` as an SQL builder easier.
+
+
+
diff --git a/exec/query_executor.go b/exec/query_executor.go
index d759f19d..e84804af 100644
--- a/exec/query_executor.go
+++ b/exec/query_executor.go
@@ -2,11 +2,11 @@ package exec
import (
"context"
- "database/sql"
+ gsql "database/sql"
"reflect"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type (
@@ -30,22 +30,26 @@ func newQueryExecutor(de DbExecutor, err error, query string, args ...interface{
return QueryExecutor{de: de, err: err, query: query, args: args}
}
-func (q QueryExecutor) Exec() (sql.Result, error) {
+func (q QueryExecutor) ToSQL() (sql string, args []interface{}, err error) {
+ return q.query, q.args, q.err
+}
+
+func (q QueryExecutor) Exec() (gsql.Result, error) {
return q.ExecContext(context.Background())
}
-func (q QueryExecutor) ExecContext(ctx context.Context) (sql.Result, error) {
+func (q QueryExecutor) ExecContext(ctx context.Context) (gsql.Result, error) {
if q.err != nil {
return nil, q.err
}
return q.de.ExecContext(ctx, q.query, q.args...)
}
-func (q QueryExecutor) Query() (*sql.Rows, error) {
+func (q QueryExecutor) Query() (*gsql.Rows, error) {
return q.QueryContext(context.Background())
}
-func (q QueryExecutor) QueryContext(ctx context.Context) (*sql.Rows, error) {
+func (q QueryExecutor) QueryContext(ctx context.Context) (*gsql.Rows, error) {
if q.err != nil {
return nil, q.err
}
@@ -204,9 +208,9 @@ func (q QueryExecutor) ScanValContext(ctx context.Context, i interface{}) (bool,
val = reflect.Indirect(val)
if util.IsSlice(val.Kind()) {
switch i.(type) {
- case *sql.RawBytes: // do nothing
+ case *gsql.RawBytes: // do nothing
case *[]byte: // do nothing
- case sql.Scanner: // do nothing
+ case gsql.Scanner: // do nothing
default:
return false, errScanValNonSlice
}
diff --git a/exec/query_factory.go b/exec/query_factory.go
index 1994ec50..235d00e0 100644
--- a/exec/query_factory.go
+++ b/exec/query_factory.go
@@ -4,7 +4,7 @@ import (
"context"
"database/sql"
- "github.com/doug-martin/goqu/v7/internal/sb"
+ "github.com/doug-martin/goqu/v8/internal/sb"
)
type (
diff --git a/exec/scanner.go b/exec/scanner.go
index e15feab2..e73f892a 100644
--- a/exec/scanner.go
+++ b/exec/scanner.go
@@ -4,9 +4,9 @@ import (
"database/sql"
"reflect"
- "github.com/doug-martin/goqu/v7/exp"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type (
diff --git a/exp/col.go b/exp/col.go
index 71327c09..cd6f686c 100644
--- a/exp/col.go
+++ b/exp/col.go
@@ -4,7 +4,7 @@ import (
"fmt"
"reflect"
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type columnList struct {
@@ -17,6 +17,8 @@ func NewColumnListExpression(vals ...interface{}) ColumnListExpression {
switch t := val.(type) {
case string:
cols = append(cols, ParseIdentifier(t))
+ case ColumnListExpression:
+ cols = append(cols, t.Columns()...)
case Expression:
cols = append(cols, t)
default:
diff --git a/exp/delete_clauses.go b/exp/delete_clauses.go
new file mode 100644
index 00000000..6d80e840
--- /dev/null
+++ b/exp/delete_clauses.go
@@ -0,0 +1,176 @@
+package exp
+
+type (
+ DeleteClauses interface {
+ HasFrom() bool
+ clone() *deleteClauses
+
+ CommonTables() []CommonTableExpression
+ CommonTablesAppend(cte CommonTableExpression) DeleteClauses
+
+ From() IdentifierExpression
+ SetFrom(table IdentifierExpression) DeleteClauses
+
+ Where() ExpressionList
+ ClearWhere() DeleteClauses
+ WhereAppend(expressions ...Expression) DeleteClauses
+
+ Order() ColumnListExpression
+ HasOrder() bool
+ ClearOrder() DeleteClauses
+ SetOrder(oes ...OrderedExpression) DeleteClauses
+ OrderAppend(...OrderedExpression) DeleteClauses
+ OrderPrepend(...OrderedExpression) DeleteClauses
+
+ Limit() interface{}
+ HasLimit() bool
+ ClearLimit() DeleteClauses
+ SetLimit(limit interface{}) DeleteClauses
+
+ Returning() ColumnListExpression
+ HasReturning() bool
+ SetReturning(cl ColumnListExpression) DeleteClauses
+ }
+ deleteClauses struct {
+ commonTables []CommonTableExpression
+ from IdentifierExpression
+ where ExpressionList
+ order ColumnListExpression
+ limit interface{}
+ returning ColumnListExpression
+ }
+)
+
+func NewDeleteClauses() DeleteClauses {
+ return &deleteClauses{}
+}
+
+func (dc *deleteClauses) HasFrom() bool {
+ return dc.from != nil
+}
+
+func (dc *deleteClauses) clone() *deleteClauses {
+ return &deleteClauses{
+ commonTables: dc.commonTables,
+ from: dc.from,
+
+ where: dc.where,
+ order: dc.order,
+ limit: dc.limit,
+ returning: dc.returning,
+ }
+}
+
+func (dc *deleteClauses) CommonTables() []CommonTableExpression {
+ return dc.commonTables
+}
+func (dc *deleteClauses) CommonTablesAppend(cte CommonTableExpression) DeleteClauses {
+ ret := dc.clone()
+ ret.commonTables = append(ret.commonTables, cte)
+ return ret
+}
+
+func (dc *deleteClauses) From() IdentifierExpression {
+ return dc.from
+}
+func (dc *deleteClauses) SetFrom(table IdentifierExpression) DeleteClauses {
+ ret := dc.clone()
+ ret.from = table
+ return ret
+}
+
+func (dc *deleteClauses) Where() ExpressionList {
+ return dc.where
+}
+
+func (dc *deleteClauses) ClearWhere() DeleteClauses {
+ ret := dc.clone()
+ ret.where = nil
+ return ret
+}
+
+func (dc *deleteClauses) WhereAppend(expressions ...Expression) DeleteClauses {
+ expLen := len(expressions)
+ if expLen == 0 {
+ return dc
+ }
+ ret := dc.clone()
+ if ret.where == nil {
+ ret.where = NewExpressionList(AndType, expressions...)
+ } else {
+ ret.where = ret.where.Append(expressions...)
+ }
+ return ret
+}
+
+func (dc *deleteClauses) Order() ColumnListExpression {
+ return dc.order
+}
+
+func (dc *deleteClauses) HasOrder() bool {
+ return dc.order != nil
+}
+
+func (dc *deleteClauses) ClearOrder() DeleteClauses {
+ ret := dc.clone()
+ ret.order = nil
+ return ret
+}
+
+func (dc *deleteClauses) SetOrder(oes ...OrderedExpression) DeleteClauses {
+ ret := dc.clone()
+ ret.order = NewOrderedColumnList(oes...)
+ return ret
+}
+
+func (dc *deleteClauses) OrderAppend(oes ...OrderedExpression) DeleteClauses {
+ if dc.order == nil {
+ return dc.SetOrder(oes...)
+ }
+ ret := dc.clone()
+ ret.order = ret.order.Append(NewOrderedColumnList(oes...).Columns()...)
+ return ret
+}
+
+func (dc *deleteClauses) OrderPrepend(oes ...OrderedExpression) DeleteClauses {
+ if dc.order == nil {
+ return dc.SetOrder(oes...)
+ }
+ ret := dc.clone()
+ ret.order = NewOrderedColumnList(oes...).Append(ret.order.Columns()...)
+ return ret
+}
+
+func (dc *deleteClauses) Limit() interface{} {
+ return dc.limit
+}
+
+func (dc *deleteClauses) HasLimit() bool {
+ return dc.limit != nil
+}
+
+func (dc *deleteClauses) ClearLimit() DeleteClauses {
+ ret := dc.clone()
+ ret.limit = nil
+ return ret
+}
+
+func (dc *deleteClauses) SetLimit(limit interface{}) DeleteClauses {
+ ret := dc.clone()
+ ret.limit = limit
+ return ret
+}
+
+func (dc *deleteClauses) Returning() ColumnListExpression {
+ return dc.returning
+}
+
+func (dc *deleteClauses) HasReturning() bool {
+ return dc.returning != nil
+}
+
+func (dc *deleteClauses) SetReturning(cl ColumnListExpression) DeleteClauses {
+ ret := dc.clone()
+ ret.returning = cl
+ return ret
+}
diff --git a/exp/delete_clauses_test.go b/exp/delete_clauses_test.go
new file mode 100644
index 00000000..5d38f9d8
--- /dev/null
+++ b/exp/delete_clauses_test.go
@@ -0,0 +1,282 @@
+package exp
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+)
+
+type deleteClausesSuite struct {
+ suite.Suite
+}
+
+func TestDeleteClausesSuite(t *testing.T) {
+ suite.Run(t, new(deleteClausesSuite))
+}
+
+func (dcs *deleteClausesSuite) TestHasFrom() {
+ t := dcs.T()
+ c := NewDeleteClauses()
+ c2 := c.SetFrom(NewIdentifierExpression("", "test", ""))
+
+ assert.False(t, c.HasFrom())
+
+ assert.True(t, c2.HasFrom())
+}
+
+func (dcs *deleteClausesSuite) TestFrom() {
+ t := dcs.T()
+ c := NewDeleteClauses()
+ ti := NewIdentifierExpression("", "a", "")
+ c2 := c.SetFrom(ti)
+
+ assert.Nil(t, c.From())
+
+ assert.Equal(t, ti, c2.From())
+}
+
+func (dcs *deleteClausesSuite) TestSetFrom() {
+ t := dcs.T()
+ c := NewDeleteClauses()
+ ti := NewIdentifierExpression("", "a", "")
+ c2 := c.SetFrom(ti)
+
+ assert.Nil(t, c.From())
+
+ assert.Equal(t, ti, c2.From())
+}
+
+func (dcs *deleteClausesSuite) TestWhere() {
+ t := dcs.T()
+ w := Ex{"a": 1}
+
+ c := NewDeleteClauses()
+ c2 := c.WhereAppend(w)
+
+ assert.Nil(t, c.Where())
+
+ assert.Equal(t, NewExpressionList(AndType, w), c2.Where())
+}
+
+func (dcs *deleteClausesSuite) TestClearWhere() {
+ t := dcs.T()
+ w := Ex{"a": 1}
+
+ c := NewDeleteClauses().WhereAppend(w)
+ c2 := c.ClearWhere()
+
+ assert.Equal(t, NewExpressionList(AndType, w), c.Where())
+
+ assert.Nil(t, c2.Where())
+}
+
+func (dcs *deleteClausesSuite) TestWhereAppend() {
+ t := dcs.T()
+ w := Ex{"a": 1}
+ w2 := Ex{"b": 2}
+
+ c := NewDeleteClauses()
+ c2 := c.WhereAppend(w)
+
+ c3 := c.WhereAppend(w).WhereAppend(w2)
+
+ c4 := c.WhereAppend(w, w2)
+
+ assert.Nil(t, c.Where())
+
+ assert.Equal(t, NewExpressionList(AndType, w), c2.Where())
+ assert.Equal(t, NewExpressionList(AndType, w).Append(w2), c3.Where())
+ assert.Equal(t, NewExpressionList(AndType, w, w2), c4.Where())
+}
+
+func (dcs *deleteClausesSuite) TestOrder() {
+ t := dcs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+
+ c := NewDeleteClauses()
+ c2 := c.SetOrder(oe)
+
+ assert.Nil(t, c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe), c2.Order())
+}
+
+func (dcs *deleteClausesSuite) TestHasOrder() {
+ t := dcs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+
+ c := NewDeleteClauses()
+ c2 := c.SetOrder(oe)
+
+ assert.False(t, c.HasOrder())
+
+ assert.True(t, c2.HasOrder())
+}
+
+func (dcs *deleteClausesSuite) TestClearOrder() {
+ t := dcs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+
+ c := NewDeleteClauses().SetOrder(oe)
+ c2 := c.ClearOrder()
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Nil(t, c2.Order())
+}
+
+func (dcs *deleteClausesSuite) TestSetOrder() {
+ t := dcs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+ oe2 := NewIdentifierExpression("", "", "b").Desc()
+
+ c := NewDeleteClauses().SetOrder(oe)
+ c2 := c.SetOrder(oe2)
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe2), c2.Order())
+}
+
+func (dcs *deleteClausesSuite) TestOrderAppend() {
+ t := dcs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+ oe2 := NewIdentifierExpression("", "", "b").Desc()
+
+ c := NewDeleteClauses().SetOrder(oe)
+ c2 := c.OrderAppend(oe2)
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe, oe2), c2.Order())
+}
+
+func (dcs *deleteClausesSuite) TestOrderPrepend() {
+ t := dcs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+ oe2 := NewIdentifierExpression("", "", "b").Desc()
+
+ c := NewDeleteClauses().SetOrder(oe)
+ c2 := c.OrderPrepend(oe2)
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe2, oe), c2.Order())
+}
+
+func (dcs *deleteClausesSuite) TestLimit() {
+ t := dcs.T()
+ l := 1
+
+ c := NewDeleteClauses()
+ c2 := c.SetLimit(l)
+
+ assert.Nil(t, c.Limit())
+
+ assert.Equal(t, l, c2.Limit())
+}
+
+func (dcs *deleteClausesSuite) TestHasLimit() {
+ t := dcs.T()
+ l := 1
+
+ c := NewDeleteClauses()
+ c2 := c.SetLimit(l)
+
+ assert.False(t, c.HasLimit())
+
+ assert.True(t, c2.HasLimit())
+}
+
+func (dcs *deleteClausesSuite) TestCLearLimit() {
+ t := dcs.T()
+ l := 1
+
+ c := NewDeleteClauses().SetLimit(l)
+ c2 := c.ClearLimit()
+
+ assert.True(t, c.HasLimit())
+
+ assert.False(t, c2.HasLimit())
+}
+
+func (dcs *deleteClausesSuite) TestSetLimit() {
+ t := dcs.T()
+ l := 1
+ l2 := 2
+
+ c := NewDeleteClauses().SetLimit(l)
+ c2 := c.SetLimit(2)
+
+ assert.Equal(t, l, c.Limit())
+
+ assert.Equal(t, l2, c2.Limit())
+}
+
+func (dcs *deleteClausesSuite) TestCommonTables() {
+ t := dcs.T()
+
+ cte := NewCommonTableExpression(true, "test", newTestAppendableExpression(`SELECT * FROM "foo"`, []interface{}{}))
+
+ c := NewDeleteClauses()
+ c2 := c.CommonTablesAppend(cte)
+
+ assert.Nil(t, c.CommonTables())
+
+ assert.Equal(t, []CommonTableExpression{cte}, c2.CommonTables())
+}
+
+func (dcs *deleteClausesSuite) TestAddCommonTablesAppend() {
+ t := dcs.T()
+
+ cte := NewCommonTableExpression(true, "test", testSQLExpression("test_cte"))
+ cte2 := NewCommonTableExpression(true, "test", testSQLExpression("test_cte2"))
+
+ c := NewDeleteClauses().CommonTablesAppend(cte)
+ c2 := c.CommonTablesAppend(cte2)
+
+ assert.Equal(t, []CommonTableExpression{cte}, c.CommonTables())
+
+ assert.Equal(t, []CommonTableExpression{cte, cte2}, c2.CommonTables())
+}
+
+func (dcs *deleteClausesSuite) TestReturning() {
+ t := dcs.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+
+ c := NewDeleteClauses()
+ c2 := c.SetReturning(cl)
+
+ assert.Nil(t, c.Returning())
+
+ assert.Equal(t, cl, c2.Returning())
+}
+
+func (dcs *deleteClausesSuite) TestHasReturning() {
+ t := dcs.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+
+ c := NewDeleteClauses()
+ c2 := c.SetReturning(cl)
+
+ assert.False(t, c.HasReturning())
+
+ assert.True(t, c2.HasReturning())
+}
+
+func (dcs *deleteClausesSuite) TestSetReturning() {
+ t := dcs.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+ cl2 := NewColumnListExpression(NewIdentifierExpression("", "", "col2"))
+
+ c := NewDeleteClauses().SetReturning(cl)
+ c2 := c.SetReturning(cl2)
+
+ assert.Equal(t, cl, c.Returning())
+
+ assert.Equal(t, cl2, c2.Returning())
+}
diff --git a/exp/exp.go b/exp/exp.go
index 30bb3485..dcc9f041 100644
--- a/exp/exp.go
+++ b/exp/exp.go
@@ -3,7 +3,7 @@ package exp
import (
"fmt"
- "github.com/doug-martin/goqu/v7/internal/sb"
+ "github.com/doug-martin/goqu/v8/internal/sb"
)
// Behaviors
@@ -130,6 +130,7 @@ type (
type (
// Alternative to writing map[string]interface{}. Can be used for Inserts, Updates or Deletes
Record map[string]interface{}
+ Vals []interface{}
// Parent of all expression types
Expression interface {
Clone() Expression
@@ -144,7 +145,7 @@ type (
AppendableExpression interface {
Expression
AppendSQL(b sb.SQLBuilder)
- GetClauses() Clauses
+ GetClauses() SelectClauses
}
// Expression for Aliased expressions
// I("a").As("b") -> "a" AS "b"
@@ -273,10 +274,6 @@ type (
SetCols(cols ColumnListExpression) InsertExpression
Vals() [][]interface{}
SetVals([][]interface{}) InsertExpression
- OnConflict() ConflictExpression
- SetOnConflict(ce ConflictExpression) InsertExpression
- DoNothing() InsertExpression
- DoUpdate(target string, update interface{}) InsertExpression
}
JoinType int
diff --git a/exp/exp_map.go b/exp/exp_map.go
index 45600115..7b15a96d 100644
--- a/exp/exp_map.go
+++ b/exp/exp_map.go
@@ -4,7 +4,7 @@ import (
"sort"
"strings"
- "github.com/doug-martin/goqu/v7/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/errors"
)
type (
diff --git a/exp/insert.go b/exp/insert.go
index 0c800a16..b43a5746 100644
--- a/exp/insert.go
+++ b/exp/insert.go
@@ -4,16 +4,15 @@ import (
"reflect"
"sort"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type (
insert struct {
- from AppendableExpression
- cols ColumnListExpression
- vals [][]interface{}
- onConflict ConflictExpression
+ from AppendableExpression
+ cols ColumnListExpression
+ vals [][]interface{}
}
)
@@ -47,7 +46,7 @@ func (i *insert) Clone() Expression {
}
func (i *insert) clone() *insert {
- return &insert{from: i.from, cols: i.cols, vals: i.vals, onConflict: i.onConflict}
+ return &insert{from: i.from, cols: i.cols, vals: i.vals}
}
func (i *insert) IsEmpty() bool {
@@ -80,24 +79,6 @@ func (i *insert) SetVals(vals [][]interface{}) InsertExpression {
return ci
}
-func (i *insert) OnConflict() ConflictExpression {
- return i.onConflict
-}
-
-func (i *insert) SetOnConflict(ce ConflictExpression) InsertExpression {
- ci := i.clone()
- ci.onConflict = ce
- return ci
-}
-
-func (i *insert) DoNothing() InsertExpression {
- return i.SetOnConflict(NewDoNothingConflictExpression())
-
-}
-func (i *insert) DoUpdate(target string, update interface{}) InsertExpression {
- return i.SetOnConflict(NewDoUpdateConflictExpression(target, update))
-}
-
// parses the rows gathering and sorting unique columns and values for each record
func newInsert(rows ...interface{}) (insertExp InsertExpression, err error) {
var mapKeys util.ValueSlice
diff --git a/exp/insert_clauses.go b/exp/insert_clauses.go
new file mode 100644
index 00000000..295486ed
--- /dev/null
+++ b/exp/insert_clauses.go
@@ -0,0 +1,182 @@
+package exp
+
+type (
+ InsertClauses interface {
+ CommonTables() []CommonTableExpression
+ CommonTablesAppend(cte CommonTableExpression) InsertClauses
+
+ HasInto() bool
+ clone() *insertClauses
+
+ Cols() ColumnListExpression
+ HasCols() bool
+ ColsAppend(cols ColumnListExpression) InsertClauses
+ SetCols(cols ColumnListExpression) InsertClauses
+
+ Into() Expression
+ SetInto(cl Expression) InsertClauses
+
+ Returning() ColumnListExpression
+ HasReturning() bool
+ SetReturning(cl ColumnListExpression) InsertClauses
+
+ From() AppendableExpression
+ HasFrom() bool
+ SetFrom(ae AppendableExpression) InsertClauses
+
+ Rows() []interface{}
+ HasRows() bool
+ SetRows(rows []interface{}) InsertClauses
+
+ Vals() [][]interface{}
+ HasVals() bool
+ SetVals(vals [][]interface{}) InsertClauses
+ ValsAppend(vals [][]interface{}) InsertClauses
+
+ OnConflict() ConflictExpression
+ SetOnConflict(expression ConflictExpression) InsertClauses
+ }
+ insertClauses struct {
+ commonTables []CommonTableExpression
+ cols ColumnListExpression
+ into Expression
+ returning ColumnListExpression
+ rows []interface{}
+ values [][]interface{}
+ from AppendableExpression
+ conflict ConflictExpression
+ }
+)
+
+func NewInsertClauses() InsertClauses {
+ return &insertClauses{}
+}
+
+func (ic *insertClauses) HasInto() bool {
+ return ic.into != nil
+}
+
+func (ic *insertClauses) clone() *insertClauses {
+ return &insertClauses{
+ commonTables: ic.commonTables,
+ cols: ic.cols,
+ into: ic.into,
+ returning: ic.returning,
+ rows: ic.rows,
+ values: ic.values,
+ from: ic.from,
+ conflict: ic.conflict,
+ }
+}
+
+func (ic *insertClauses) CommonTables() []CommonTableExpression {
+ return ic.commonTables
+}
+func (ic *insertClauses) CommonTablesAppend(cte CommonTableExpression) InsertClauses {
+ ret := ic.clone()
+ ret.commonTables = append(ret.commonTables, cte)
+ return ret
+}
+
+func (ic *insertClauses) Cols() ColumnListExpression {
+ return ic.cols
+}
+
+func (ic *insertClauses) HasCols() bool {
+ return ic.cols != nil && !ic.cols.IsEmpty()
+}
+
+func (ic *insertClauses) ColsAppend(cl ColumnListExpression) InsertClauses {
+ ret := ic.clone()
+ ret.cols = ret.cols.Append(cl.Columns()...)
+ return ret
+}
+
+func (ic *insertClauses) SetCols(cl ColumnListExpression) InsertClauses {
+ ret := ic.clone()
+ ret.cols = cl
+ return ret
+}
+
+func (ic *insertClauses) Into() Expression {
+ return ic.into
+}
+func (ic *insertClauses) SetInto(into Expression) InsertClauses {
+ ret := ic.clone()
+ ret.into = into
+ return ret
+}
+
+func (ic *insertClauses) Returning() ColumnListExpression {
+ return ic.returning
+}
+
+func (ic *insertClauses) HasReturning() bool {
+ return ic.returning != nil
+}
+
+func (ic *insertClauses) SetReturning(cl ColumnListExpression) InsertClauses {
+ ret := ic.clone()
+ ret.returning = cl
+ return ret
+}
+
+func (ic *insertClauses) From() AppendableExpression {
+ return ic.from
+}
+
+func (ic *insertClauses) HasFrom() bool {
+ return ic.from != nil
+}
+
+func (ic *insertClauses) SetFrom(ae AppendableExpression) InsertClauses {
+ ret := ic.clone()
+ ret.from = ae
+ return ret
+}
+
+func (ic *insertClauses) Rows() []interface{} {
+ return ic.rows
+}
+
+func (ic *insertClauses) HasRows() bool {
+ return ic.rows != nil && len(ic.rows) > 0
+}
+
+func (ic *insertClauses) SetRows(rows []interface{}) InsertClauses {
+ ret := ic.clone()
+ ret.rows = rows
+ return ret
+}
+
+func (ic *insertClauses) Vals() [][]interface{} {
+ return ic.values
+}
+
+func (ic *insertClauses) HasVals() bool {
+ return ic.values != nil && len(ic.values) > 0
+}
+
+func (ic *insertClauses) SetVals(vals [][]interface{}) InsertClauses {
+ ret := ic.clone()
+ ret.values = vals
+ return ret
+}
+
+func (ic *insertClauses) ValsAppend(vals [][]interface{}) InsertClauses {
+ newVals := make([][]interface{}, len(ic.values))
+ copy(newVals, ic.values)
+ ret := ic.clone()
+ newVals = append(newVals, vals...)
+ ret.values = newVals
+ return ret
+}
+
+func (ic *insertClauses) OnConflict() ConflictExpression {
+ return ic.conflict
+}
+func (ic *insertClauses) SetOnConflict(expression ConflictExpression) InsertClauses {
+ ret := ic.clone()
+ ret.conflict = expression
+ return ret
+}
diff --git a/exp/insert_clauses_test.go b/exp/insert_clauses_test.go
new file mode 100644
index 00000000..e760fb2c
--- /dev/null
+++ b/exp/insert_clauses_test.go
@@ -0,0 +1,267 @@
+package exp
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+)
+
+type insertClausesSuite struct {
+ suite.Suite
+}
+
+func TestInsertClausesSuite(t *testing.T) {
+ suite.Run(t, new(insertClausesSuite))
+}
+
+func (ics *insertClausesSuite) TestInto() {
+ t := ics.T()
+ c := NewInsertClauses()
+ ti := NewIdentifierExpression("", "test", "")
+ c2 := c.SetInto(ti)
+
+ assert.Nil(t, c.Into())
+
+ assert.Equal(t, ti, c2.Into())
+}
+
+func (ics *insertClausesSuite) TestHasInto() {
+ t := ics.T()
+ c := NewInsertClauses()
+ ti := NewIdentifierExpression("", "test", "")
+ c2 := c.SetInto(ti)
+
+ assert.False(t, c.HasInto())
+
+ assert.True(t, c2.HasInto())
+}
+
+func (ics *insertClausesSuite) TestFrom() {
+ t := ics.T()
+ c := NewInsertClauses()
+ ae := newTestAppendableExpression("select * from test", nil)
+ c2 := c.SetFrom(ae)
+
+ assert.Nil(t, c.From())
+
+ assert.Equal(t, ae, c2.From())
+}
+
+func (ics *insertClausesSuite) TestHasFrom() {
+ t := ics.T()
+ c := NewInsertClauses()
+ ae := newTestAppendableExpression("select * from test", nil)
+ c2 := c.SetFrom(ae)
+
+ assert.False(t, c.HasFrom())
+
+ assert.True(t, c2.HasFrom())
+}
+
+func (ics *insertClausesSuite) TestSetFrom() {
+ t := ics.T()
+ c := NewInsertClauses()
+ ae := newTestAppendableExpression("select * from test", nil)
+ c2 := c.SetFrom(ae)
+
+ assert.Nil(t, c.From())
+
+ assert.Equal(t, ae, c2.From())
+}
+
+func (ics *insertClausesSuite) TestCols() {
+ t := ics.T()
+ c := NewInsertClauses()
+ cle := NewColumnListExpression("a", "b")
+ c2 := c.SetCols(cle)
+
+ assert.Nil(t, c.Cols())
+
+ assert.Equal(t, cle, c2.Cols())
+}
+
+func (ics *insertClausesSuite) TestHasCols() {
+ t := ics.T()
+ c := NewInsertClauses()
+ cle := NewColumnListExpression("a", "b")
+ c2 := c.SetCols(cle)
+
+ assert.False(t, c.HasCols())
+
+ assert.True(t, c2.HasCols())
+}
+func (ics *insertClausesSuite) TestColsAppend() {
+ t := ics.T()
+ cle := NewColumnListExpression("a")
+ cle2 := NewColumnListExpression("b")
+ c := NewInsertClauses().SetCols(cle)
+ c2 := c.ColsAppend(cle2)
+
+ assert.Equal(t, cle, c.Cols())
+
+ assert.Equal(t, NewColumnListExpression("a", "b"), c2.Cols())
+}
+
+func (ics *insertClausesSuite) TestVals() {
+ t := ics.T()
+ c := NewInsertClauses()
+ vals := [][]interface{}{{"a", "b"}}
+ c2 := c.SetVals(vals)
+
+ assert.Nil(t, c.Vals())
+
+ assert.Equal(t, vals, c2.Vals())
+}
+
+func (ics *insertClausesSuite) TestHasVals() {
+ t := ics.T()
+ c := NewInsertClauses()
+ vals := [][]interface{}{{"a", "b"}}
+ c2 := c.SetVals(vals)
+
+ assert.False(t, c.HasVals())
+
+ assert.True(t, c2.HasVals())
+}
+func (ics *insertClausesSuite) TestValsAppend() {
+ t := ics.T()
+ vals := [][]interface{}{{"a", "b"}}
+ vals2 := [][]interface{}{{"c", "d"}}
+ c := NewInsertClauses().SetVals(vals)
+ c2 := c.ValsAppend(vals2)
+
+ assert.Equal(t, vals, c.Vals())
+
+ assert.Equal(t, [][]interface{}{
+ {"a", "b"},
+ {"c", "d"},
+ }, c2.Vals())
+}
+
+func (ics *insertClausesSuite) TestRows() {
+ t := ics.T()
+ c := NewInsertClauses()
+ rs := []interface{}{Record{"a": "a1", "b": "b1"}}
+ c2 := c.SetRows(rs)
+
+ assert.Nil(t, c.Rows())
+
+ assert.Equal(t, rs, c2.Rows())
+}
+
+func (ics *insertClausesSuite) TestHasRows() {
+ t := ics.T()
+ c := NewInsertClauses()
+ rs := []interface{}{Record{"a": "a1", "b": "b1"}}
+ c2 := c.SetRows(rs)
+
+ assert.False(t, c.HasRows())
+
+ assert.True(t, c2.HasRows())
+}
+func (ics *insertClausesSuite) TestSetRows() {
+ t := ics.T()
+ rs := []interface{}{Record{"a": "a1", "b": "b1"}}
+ c := NewInsertClauses().SetRows(rs)
+ rs2 := []interface{}{Record{"a": "a2", "b": "b2"}}
+ c2 := c.SetRows(rs2)
+
+ assert.Equal(t, rs, c.Rows())
+
+ assert.Equal(t, rs2, c2.Rows())
+}
+
+func (ics *insertClausesSuite) TestCommonTables() {
+ t := ics.T()
+
+ cte := NewCommonTableExpression(true, "test", newTestAppendableExpression(`SELECT * FROM "foo"`, []interface{}{}))
+
+ c := NewInsertClauses()
+ c2 := c.CommonTablesAppend(cte)
+
+ assert.Nil(t, c.CommonTables())
+
+ assert.Equal(t, []CommonTableExpression{cte}, c2.CommonTables())
+}
+
+func (ics *insertClausesSuite) TestAddCommonTablesAppend() {
+ t := ics.T()
+
+ cte := NewCommonTableExpression(true, "test", testSQLExpression("test_cte"))
+ cte2 := NewCommonTableExpression(true, "test", testSQLExpression("test_cte2"))
+
+ c := NewInsertClauses().CommonTablesAppend(cte)
+ c2 := c.CommonTablesAppend(cte2)
+
+ assert.Equal(t, []CommonTableExpression{cte}, c.CommonTables())
+
+ assert.Equal(t, []CommonTableExpression{cte, cte2}, c2.CommonTables())
+}
+
+func (ics *insertClausesSuite) TestOnConflict() {
+ t := ics.T()
+
+ ce := NewDoNothingConflictExpression()
+
+ c := NewInsertClauses()
+ c2 := c.SetOnConflict(ce)
+
+ assert.Nil(t, c.OnConflict())
+
+ assert.Equal(t, ce, c2.OnConflict())
+}
+
+func (ics *insertClausesSuite) TestSetOnConflict() {
+ t := ics.T()
+
+ ce := NewDoNothingConflictExpression()
+
+ c := NewInsertClauses().SetOnConflict(ce)
+ ce2 := NewDoUpdateConflictExpression("test", Record{"a": "a1"})
+ c2 := c.SetOnConflict(ce2)
+
+ assert.Equal(t, ce, c.OnConflict())
+
+ assert.Equal(t, ce2, c2.OnConflict())
+}
+
+func (ics *insertClausesSuite) TestReturning() {
+ t := ics.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+
+ c := NewInsertClauses()
+ c2 := c.SetReturning(cl)
+
+ assert.Nil(t, c.Returning())
+
+ assert.Equal(t, cl, c2.Returning())
+}
+
+func (ics *insertClausesSuite) TestHasReturning() {
+ t := ics.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+
+ c := NewInsertClauses()
+ c2 := c.SetReturning(cl)
+
+ assert.False(t, c.HasReturning())
+
+ assert.True(t, c2.HasReturning())
+}
+
+func (ics *insertClausesSuite) TestSetReturning() {
+ t := ics.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+ cl2 := NewColumnListExpression(NewIdentifierExpression("", "", "col2"))
+
+ c := NewInsertClauses().SetReturning(cl)
+ c2 := c.SetReturning(cl2)
+
+ assert.Equal(t, cl, c.Returning())
+
+ assert.Equal(t, cl2, c2.Returning())
+}
diff --git a/exp/insert_test.go b/exp/insert_test.go
index c50c388c..748e74cc 100644
--- a/exp/insert_test.go
+++ b/exp/insert_test.go
@@ -11,7 +11,7 @@ type testAppendableExpression struct {
AppendableExpression
sql string
args []interface{}
- clauses Clauses
+ clauses SelectClauses
}
func newTestAppendableExpression(sql string, args []interface{}) AppendableExpression {
@@ -22,7 +22,7 @@ func (tae *testAppendableExpression) Expression() Expression {
return tae
}
-func (tae *testAppendableExpression) GetClauses() Clauses {
+func (tae *testAppendableExpression) GetClauses() SelectClauses {
return tae.clauses
}
diff --git a/exp/clauses.go b/exp/select_clauses.go
similarity index 53%
rename from exp/clauses.go
rename to exp/select_clauses.go
index 93cf2e80..be1e7097 100644
--- a/exp/clauses.go
+++ b/exp/select_clauses.go
@@ -1,70 +1,66 @@
package exp
type (
- Clauses interface {
+ SelectClauses interface {
HasSources() bool
IsDefaultSelect() bool
- clone() *clauses
+ clone() *selectClauses
Select() ColumnListExpression
- SelectAppend(cl ColumnListExpression) Clauses
- SetSelect(cl ColumnListExpression) Clauses
+ SelectAppend(cl ColumnListExpression) SelectClauses
+ SetSelect(cl ColumnListExpression) SelectClauses
SelectDistinct() ColumnListExpression
HasSelectDistinct() bool
- SetSelectDistinct(cl ColumnListExpression) Clauses
+ SetSelectDistinct(cl ColumnListExpression) SelectClauses
From() ColumnListExpression
- SetFrom(cl ColumnListExpression) Clauses
+ SetFrom(cl ColumnListExpression) SelectClauses
HasAlias() bool
Alias() IdentifierExpression
- SetAlias(ie IdentifierExpression) Clauses
+ SetAlias(ie IdentifierExpression) SelectClauses
Joins() JoinExpressions
- JoinsAppend(jc JoinExpression) Clauses
+ JoinsAppend(jc JoinExpression) SelectClauses
Where() ExpressionList
- ClearWhere() Clauses
- WhereAppend(expressions ...Expression) Clauses
+ ClearWhere() SelectClauses
+ WhereAppend(expressions ...Expression) SelectClauses
Having() ExpressionList
- ClearHaving() Clauses
- HavingAppend(expressions ...Expression) Clauses
+ ClearHaving() SelectClauses
+ HavingAppend(expressions ...Expression) SelectClauses
Order() ColumnListExpression
HasOrder() bool
- ClearOrder() Clauses
- SetOrder(oes ...OrderedExpression) Clauses
- OrderAppend(...OrderedExpression) Clauses
- OrderPrepend(...OrderedExpression) Clauses
+ ClearOrder() SelectClauses
+ SetOrder(oes ...OrderedExpression) SelectClauses
+ OrderAppend(...OrderedExpression) SelectClauses
+ OrderPrepend(...OrderedExpression) SelectClauses
GroupBy() ColumnListExpression
- SetGroupBy(cl ColumnListExpression) Clauses
+ SetGroupBy(cl ColumnListExpression) SelectClauses
Limit() interface{}
HasLimit() bool
- ClearLimit() Clauses
- SetLimit(limit interface{}) Clauses
+ ClearLimit() SelectClauses
+ SetLimit(limit interface{}) SelectClauses
Offset() uint
- ClearOffset() Clauses
- SetOffset(offset uint) Clauses
+ ClearOffset() SelectClauses
+ SetOffset(offset uint) SelectClauses
Compounds() []CompoundExpression
- CompoundsAppend(ce CompoundExpression) Clauses
+ CompoundsAppend(ce CompoundExpression) SelectClauses
Lock() Lock
- SetLock(l Lock) Clauses
+ SetLock(l Lock) SelectClauses
CommonTables() []CommonTableExpression
- CommonTablesAppend(cte CommonTableExpression) Clauses
-
- Returning() ColumnListExpression
- HasReturning() bool
- SetReturning(cl ColumnListExpression) Clauses
+ CommonTablesAppend(cte CommonTableExpression) SelectClauses
}
- clauses struct {
+ selectClauses struct {
commonTables []CommonTableExpression
selectColumns ColumnListExpression
selectDistinct ColumnListExpression
@@ -77,23 +73,22 @@ type (
order ColumnListExpression
limit interface{}
offset uint
- returning ColumnListExpression
compounds []CompoundExpression
lock Lock
}
)
-func NewClauses() Clauses {
- return &clauses{
+func NewSelectClauses() SelectClauses {
+ return &selectClauses{
selectColumns: NewColumnListExpression(Star()),
}
}
-func (c *clauses) HasSources() bool {
+func (c *selectClauses) HasSources() bool {
return c.from != nil && len(c.from.Columns()) > 0
}
-func (c *clauses) IsDefaultSelect() bool {
+func (c *selectClauses) IsDefaultSelect() bool {
ret := false
if c.selectColumns != nil {
selects := c.selectColumns.Columns()
@@ -106,8 +101,8 @@ func (c *clauses) IsDefaultSelect() bool {
return ret
}
-func (c *clauses) clone() *clauses {
- return &clauses{
+func (c *selectClauses) clone() *selectClauses {
+ return &selectClauses{
commonTables: c.commonTables,
selectColumns: c.selectColumns,
selectDistinct: c.selectDistinct,
@@ -120,25 +115,24 @@ func (c *clauses) clone() *clauses {
order: c.order,
limit: c.limit,
offset: c.offset,
- returning: c.returning,
compounds: c.compounds,
lock: c.lock,
}
}
-func (c *clauses) CommonTables() []CommonTableExpression {
+func (c *selectClauses) CommonTables() []CommonTableExpression {
return c.commonTables
}
-func (c *clauses) CommonTablesAppend(cte CommonTableExpression) Clauses {
+func (c *selectClauses) CommonTablesAppend(cte CommonTableExpression) SelectClauses {
ret := c.clone()
ret.commonTables = append(ret.commonTables, cte)
return ret
}
-func (c *clauses) Select() ColumnListExpression {
+func (c *selectClauses) Select() ColumnListExpression {
return c.selectColumns
}
-func (c *clauses) SelectAppend(cl ColumnListExpression) Clauses {
+func (c *selectClauses) SelectAppend(cl ColumnListExpression) SelectClauses {
ret := c.clone()
if ret.selectDistinct != nil {
ret.selectDistinct = ret.selectDistinct.Append(cl.Columns()...)
@@ -148,69 +142,69 @@ func (c *clauses) SelectAppend(cl ColumnListExpression) Clauses {
return ret
}
-func (c *clauses) SetSelect(cl ColumnListExpression) Clauses {
+func (c *selectClauses) SetSelect(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.selectDistinct = nil
ret.selectColumns = cl
return ret
}
-func (c *clauses) SelectDistinct() ColumnListExpression {
+func (c *selectClauses) SelectDistinct() ColumnListExpression {
return c.selectDistinct
}
-func (c *clauses) HasSelectDistinct() bool {
+func (c *selectClauses) HasSelectDistinct() bool {
return c.selectDistinct != nil
}
-func (c *clauses) SetSelectDistinct(cl ColumnListExpression) Clauses {
+func (c *selectClauses) SetSelectDistinct(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.selectColumns = nil
ret.selectDistinct = cl
return ret
}
-func (c *clauses) From() ColumnListExpression {
+func (c *selectClauses) From() ColumnListExpression {
return c.from
}
-func (c *clauses) SetFrom(cl ColumnListExpression) Clauses {
+func (c *selectClauses) SetFrom(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.from = cl
return ret
}
-func (c *clauses) HasAlias() bool {
+func (c *selectClauses) HasAlias() bool {
return c.alias != nil
}
-func (c *clauses) Alias() IdentifierExpression {
+func (c *selectClauses) Alias() IdentifierExpression {
return c.alias
}
-func (c *clauses) SetAlias(ie IdentifierExpression) Clauses {
+func (c *selectClauses) SetAlias(ie IdentifierExpression) SelectClauses {
ret := c.clone()
ret.alias = ie
return ret
}
-func (c *clauses) Joins() JoinExpressions {
+func (c *selectClauses) Joins() JoinExpressions {
return c.joins
}
-func (c *clauses) JoinsAppend(jc JoinExpression) Clauses {
+func (c *selectClauses) JoinsAppend(jc JoinExpression) SelectClauses {
ret := c.clone()
ret.joins = append(ret.joins, jc)
return ret
}
-func (c *clauses) Where() ExpressionList {
+func (c *selectClauses) Where() ExpressionList {
return c.where
}
-func (c *clauses) ClearWhere() Clauses {
+func (c *selectClauses) ClearWhere() SelectClauses {
ret := c.clone()
ret.where = nil
return ret
}
-func (c *clauses) WhereAppend(expressions ...Expression) Clauses {
+func (c *selectClauses) WhereAppend(expressions ...Expression) SelectClauses {
expLen := len(expressions)
if expLen == 0 {
return c
@@ -224,17 +218,17 @@ func (c *clauses) WhereAppend(expressions ...Expression) Clauses {
return ret
}
-func (c *clauses) Having() ExpressionList {
+func (c *selectClauses) Having() ExpressionList {
return c.having
}
-func (c *clauses) ClearHaving() Clauses {
+func (c *selectClauses) ClearHaving() SelectClauses {
ret := c.clone()
ret.having = nil
return ret
}
-func (c *clauses) HavingAppend(expressions ...Expression) Clauses {
+func (c *selectClauses) HavingAppend(expressions ...Expression) SelectClauses {
expLen := len(expressions)
if expLen == 0 {
return c
@@ -248,36 +242,36 @@ func (c *clauses) HavingAppend(expressions ...Expression) Clauses {
return ret
}
-func (c *clauses) Lock() Lock {
+func (c *selectClauses) Lock() Lock {
return c.lock
}
-func (c *clauses) SetLock(l Lock) Clauses {
+func (c *selectClauses) SetLock(l Lock) SelectClauses {
ret := c.clone()
ret.lock = l
return ret
}
-func (c *clauses) Order() ColumnListExpression {
+func (c *selectClauses) Order() ColumnListExpression {
return c.order
}
-func (c *clauses) HasOrder() bool {
+func (c *selectClauses) HasOrder() bool {
return c.order != nil
}
-func (c *clauses) ClearOrder() Clauses {
+func (c *selectClauses) ClearOrder() SelectClauses {
ret := c.clone()
ret.order = nil
return ret
}
-func (c *clauses) SetOrder(oes ...OrderedExpression) Clauses {
+func (c *selectClauses) SetOrder(oes ...OrderedExpression) SelectClauses {
ret := c.clone()
ret.order = NewOrderedColumnList(oes...)
return ret
}
-func (c *clauses) OrderAppend(oes ...OrderedExpression) Clauses {
+func (c *selectClauses) OrderAppend(oes ...OrderedExpression) SelectClauses {
if c.order == nil {
return c.SetOrder(oes...)
}
@@ -286,7 +280,7 @@ func (c *clauses) OrderAppend(oes ...OrderedExpression) Clauses {
return ret
}
-func (c *clauses) OrderPrepend(oes ...OrderedExpression) Clauses {
+func (c *selectClauses) OrderPrepend(oes ...OrderedExpression) SelectClauses {
if c.order == nil {
return c.SetOrder(oes...)
}
@@ -295,69 +289,55 @@ func (c *clauses) OrderPrepend(oes ...OrderedExpression) Clauses {
return ret
}
-func (c *clauses) GroupBy() ColumnListExpression {
+func (c *selectClauses) GroupBy() ColumnListExpression {
return c.groupBy
}
-func (c *clauses) SetGroupBy(cl ColumnListExpression) Clauses {
+func (c *selectClauses) SetGroupBy(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.groupBy = cl
return ret
}
-func (c *clauses) Limit() interface{} {
+func (c *selectClauses) Limit() interface{} {
return c.limit
}
-func (c *clauses) HasLimit() bool {
+func (c *selectClauses) HasLimit() bool {
return c.limit != nil
}
-func (c *clauses) ClearLimit() Clauses {
+func (c *selectClauses) ClearLimit() SelectClauses {
ret := c.clone()
ret.limit = nil
return ret
}
-func (c *clauses) SetLimit(limit interface{}) Clauses {
+func (c *selectClauses) SetLimit(limit interface{}) SelectClauses {
ret := c.clone()
ret.limit = limit
return ret
}
-func (c *clauses) Offset() uint {
+func (c *selectClauses) Offset() uint {
return c.offset
}
-func (c *clauses) ClearOffset() Clauses {
+func (c *selectClauses) ClearOffset() SelectClauses {
ret := c.clone()
ret.offset = 0
return ret
}
-func (c *clauses) SetOffset(offset uint) Clauses {
+func (c *selectClauses) SetOffset(offset uint) SelectClauses {
ret := c.clone()
ret.offset = offset
return ret
}
-func (c *clauses) Compounds() []CompoundExpression {
+func (c *selectClauses) Compounds() []CompoundExpression {
return c.compounds
}
-func (c *clauses) CompoundsAppend(ce CompoundExpression) Clauses {
+func (c *selectClauses) CompoundsAppend(ce CompoundExpression) SelectClauses {
ret := c.clone()
ret.compounds = append(ret.compounds, ce)
return ret
}
-
-func (c *clauses) Returning() ColumnListExpression {
- return c.returning
-}
-
-func (c *clauses) HasReturning() bool {
- return c.returning != nil
-}
-
-func (c *clauses) SetReturning(cl ColumnListExpression) Clauses {
- ret := c.clone()
- ret.returning = cl
- return ret
-}
diff --git a/exp/clauses_test.go b/exp/select_clauses_test.go
similarity index 64%
rename from exp/clauses_test.go
rename to exp/select_clauses_test.go
index b9b6b592..09b793ea 100644
--- a/exp/clauses_test.go
+++ b/exp/select_clauses_test.go
@@ -21,17 +21,17 @@ func (tse testSQLExpression) ToSQL() (sql string, args []interface{}, err error)
return "", nil, nil
}
-type clausesTest struct {
+type selectClausesTest struct {
suite.Suite
}
-func TestClausesSuite(t *testing.T) {
- suite.Run(t, new(clausesTest))
+func TestSelectClausesSuite(t *testing.T) {
+ suite.Run(t, new(selectClausesTest))
}
-func (ct *clausesTest) TestHasSources() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestHasSources() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetFrom(NewColumnListExpression("test"))
assert.False(t, c.HasSources())
@@ -39,9 +39,9 @@ func (ct *clausesTest) TestHasSources() {
assert.True(t, c2.HasSources())
}
-func (ct *clausesTest) TestIsDefaultSelect() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestIsDefaultSelect() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SelectAppend(NewColumnListExpression("a"))
assert.True(t, c.IsDefaultSelect())
@@ -49,9 +49,9 @@ func (ct *clausesTest) TestIsDefaultSelect() {
assert.False(t, c2.IsDefaultSelect())
}
-func (ct *clausesTest) TestSelect() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSelect() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetSelect(NewColumnListExpression("a"))
assert.Equal(t, NewColumnListExpression(Star()), c.Select())
@@ -59,9 +59,9 @@ func (ct *clausesTest) TestSelect() {
assert.Equal(t, NewColumnListExpression("a"), c2.Select())
}
-func (ct *clausesTest) TestSelectAppend() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSelectAppend() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SelectAppend(NewColumnListExpression("a"))
assert.Equal(t, NewColumnListExpression(Star()), c.Select())
@@ -71,9 +71,9 @@ func (ct *clausesTest) TestSelectAppend() {
assert.Nil(t, c2.SelectDistinct())
}
-func (ct *clausesTest) TestSetSelect() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSetSelect() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetSelect(NewColumnListExpression("a"))
assert.Equal(t, NewColumnListExpression(Star()), c.Select())
@@ -84,9 +84,9 @@ func (ct *clausesTest) TestSetSelect() {
}
-func (ct *clausesTest) TestSelectDistinct() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSelectDistinct() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetSelectDistinct(NewColumnListExpression("a"))
assert.Nil(t, c.SelectDistinct())
@@ -96,9 +96,9 @@ func (ct *clausesTest) TestSelectDistinct() {
assert.Nil(t, c2.Select())
}
-func (ct *clausesTest) TestSetSelectDistinct() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSetSelectDistinct() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetSelectDistinct(NewColumnListExpression("a"))
assert.Nil(t, c.SelectDistinct())
@@ -108,9 +108,9 @@ func (ct *clausesTest) TestSetSelectDistinct() {
assert.Nil(t, c2.Select())
}
-func (ct *clausesTest) TestFrom() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestFrom() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetFrom(NewColumnListExpression("a"))
assert.Nil(t, c.From())
@@ -118,18 +118,18 @@ func (ct *clausesTest) TestFrom() {
assert.Equal(t, NewColumnListExpression("a"), c2.From())
}
-func (ct *clausesTest) TestSetFrom() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSetFrom() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetFrom(NewColumnListExpression("a"))
assert.Nil(t, c.From())
assert.Equal(t, NewColumnListExpression("a"), c2.From())
}
-func (ct *clausesTest) TestHasAlias() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestHasAlias() {
+ t := sct.T()
+ c := NewSelectClauses()
c2 := c.SetAlias(NewIdentifierExpression("", "", "a"))
assert.False(t, c.HasAlias())
@@ -137,9 +137,9 @@ func (ct *clausesTest) TestHasAlias() {
assert.True(t, c2.HasAlias())
}
-func (ct *clausesTest) TestAlias() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestAlias() {
+ t := sct.T()
+ c := NewSelectClauses()
a := NewIdentifierExpression("", "a", "")
c2 := c.SetAlias(a)
@@ -148,9 +148,9 @@ func (ct *clausesTest) TestAlias() {
assert.Equal(t, a, c2.Alias())
}
-func (ct *clausesTest) TestSetAlias() {
- t := ct.T()
- c := NewClauses()
+func (sct *selectClausesTest) TestSetAlias() {
+ t := sct.T()
+ c := NewSelectClauses()
a := NewIdentifierExpression("", "a", "")
c2 := c.SetAlias(a)
@@ -159,15 +159,15 @@ func (ct *clausesTest) TestSetAlias() {
assert.Equal(t, a, c2.Alias())
}
-func (ct *clausesTest) TestJoins() {
- t := ct.T()
+func (sct *selectClausesTest) TestJoins() {
+ t := sct.T()
jc := NewConditionedJoinExpression(
LeftJoinType,
NewIdentifierExpression("", "test", ""),
nil,
)
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.JoinsAppend(jc)
assert.Nil(t, c.Joins())
@@ -175,8 +175,8 @@ func (ct *clausesTest) TestJoins() {
assert.Equal(t, JoinExpressions{jc}, c2.Joins())
}
-func (ct *clausesTest) TestJoinsAppend() {
- t := ct.T()
+func (sct *selectClausesTest) TestJoinsAppend() {
+ t := sct.T()
jc := NewConditionedJoinExpression(
LeftJoinType,
NewIdentifierExpression("", "test", ""),
@@ -186,7 +186,7 @@ func (ct *clausesTest) TestJoinsAppend() {
LeftJoinType,
NewIdentifierExpression("", "test", ""),
)
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.JoinsAppend(jc)
c3 := c2.JoinsAppend(jc2)
@@ -196,11 +196,11 @@ func (ct *clausesTest) TestJoinsAppend() {
assert.Equal(t, JoinExpressions{jc, jc2}, c3.Joins())
}
-func (ct *clausesTest) TestWhere() {
- t := ct.T()
+func (sct *selectClausesTest) TestWhere() {
+ t := sct.T()
w := Ex{"a": 1}
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.WhereAppend(w)
assert.Nil(t, c.Where())
@@ -208,11 +208,11 @@ func (ct *clausesTest) TestWhere() {
assert.Equal(t, NewExpressionList(AndType, w), c2.Where())
}
-func (ct *clausesTest) TestClearWhere() {
- t := ct.T()
+func (sct *selectClausesTest) TestClearWhere() {
+ t := sct.T()
w := Ex{"a": 1}
- c := NewClauses().WhereAppend(w)
+ c := NewSelectClauses().WhereAppend(w)
c2 := c.ClearWhere()
assert.Equal(t, NewExpressionList(AndType, w), c.Where())
@@ -220,12 +220,12 @@ func (ct *clausesTest) TestClearWhere() {
assert.Nil(t, c2.Where())
}
-func (ct *clausesTest) TestWhereAppend() {
- t := ct.T()
+func (sct *selectClausesTest) TestWhereAppend() {
+ t := sct.T()
w := Ex{"a": 1}
w2 := Ex{"b": 2}
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.WhereAppend(w)
c3 := c.WhereAppend(w).WhereAppend(w2)
@@ -239,11 +239,11 @@ func (ct *clausesTest) TestWhereAppend() {
assert.Equal(t, NewExpressionList(AndType, w, w2), c4.Where())
}
-func (ct *clausesTest) TestHaving() {
- t := ct.T()
+func (sct *selectClausesTest) TestHaving() {
+ t := sct.T()
w := Ex{"a": 1}
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.HavingAppend(w)
assert.Nil(t, c.Having())
@@ -251,11 +251,11 @@ func (ct *clausesTest) TestHaving() {
assert.Equal(t, NewExpressionList(AndType, w), c2.Having())
}
-func (ct *clausesTest) TestClearHaving() {
- t := ct.T()
+func (sct *selectClausesTest) TestClearHaving() {
+ t := sct.T()
w := Ex{"a": 1}
- c := NewClauses().HavingAppend(w)
+ c := NewSelectClauses().HavingAppend(w)
c2 := c.ClearHaving()
assert.Equal(t, NewExpressionList(AndType, w), c.Having())
@@ -263,12 +263,12 @@ func (ct *clausesTest) TestClearHaving() {
assert.Nil(t, c2.Having())
}
-func (ct *clausesTest) TestHavingAppend() {
- t := ct.T()
+func (sct *selectClausesTest) TestHavingAppend() {
+ t := sct.T()
w := Ex{"a": 1}
w2 := Ex{"b": 2}
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.HavingAppend(w)
c3 := c.HavingAppend(w).HavingAppend(w2)
@@ -282,11 +282,11 @@ func (ct *clausesTest) TestHavingAppend() {
assert.Equal(t, NewExpressionList(AndType, w, w2), c4.Having())
}
-func (ct *clausesTest) TestOrder() {
- t := ct.T()
+func (sct *selectClausesTest) TestOrder() {
+ t := sct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetOrder(oe)
assert.Nil(t, c.Order())
@@ -294,11 +294,11 @@ func (ct *clausesTest) TestOrder() {
assert.Equal(t, NewColumnListExpression(oe), c2.Order())
}
-func (ct *clausesTest) TestHasOrder() {
- t := ct.T()
+func (sct *selectClausesTest) TestHasOrder() {
+ t := sct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetOrder(oe)
assert.False(t, c.HasOrder())
@@ -306,11 +306,11 @@ func (ct *clausesTest) TestHasOrder() {
assert.True(t, c2.HasOrder())
}
-func (ct *clausesTest) TestClearOrder() {
- t := ct.T()
+func (sct *selectClausesTest) TestClearOrder() {
+ t := sct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
- c := NewClauses().SetOrder(oe)
+ c := NewSelectClauses().SetOrder(oe)
c2 := c.ClearOrder()
assert.Equal(t, NewColumnListExpression(oe), c.Order())
@@ -318,12 +318,12 @@ func (ct *clausesTest) TestClearOrder() {
assert.Nil(t, c2.Order())
}
-func (ct *clausesTest) TestSetOrder() {
- t := ct.T()
+func (sct *selectClausesTest) TestSetOrder() {
+ t := sct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
oe2 := NewIdentifierExpression("", "", "b").Desc()
- c := NewClauses().SetOrder(oe)
+ c := NewSelectClauses().SetOrder(oe)
c2 := c.SetOrder(oe2)
assert.Equal(t, NewColumnListExpression(oe), c.Order())
@@ -331,12 +331,12 @@ func (ct *clausesTest) TestSetOrder() {
assert.Equal(t, NewColumnListExpression(oe2), c2.Order())
}
-func (ct *clausesTest) TestOrderAppend() {
- t := ct.T()
+func (sct *selectClausesTest) TestOrderAppend() {
+ t := sct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
oe2 := NewIdentifierExpression("", "", "b").Desc()
- c := NewClauses().SetOrder(oe)
+ c := NewSelectClauses().SetOrder(oe)
c2 := c.OrderAppend(oe2)
assert.Equal(t, NewColumnListExpression(oe), c.Order())
@@ -344,12 +344,12 @@ func (ct *clausesTest) TestOrderAppend() {
assert.Equal(t, NewColumnListExpression(oe, oe2), c2.Order())
}
-func (ct *clausesTest) TestOrderPrepend() {
- t := ct.T()
+func (sct *selectClausesTest) TestOrderPrepend() {
+ t := sct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
oe2 := NewIdentifierExpression("", "", "b").Desc()
- c := NewClauses().SetOrder(oe)
+ c := NewSelectClauses().SetOrder(oe)
c2 := c.OrderPrepend(oe2)
assert.Equal(t, NewColumnListExpression(oe), c.Order())
@@ -357,11 +357,11 @@ func (ct *clausesTest) TestOrderPrepend() {
assert.Equal(t, NewColumnListExpression(oe2, oe), c2.Order())
}
-func (ct *clausesTest) TestGroupBy() {
- t := ct.T()
+func (sct *selectClausesTest) TestGroupBy() {
+ t := sct.T()
g := NewColumnListExpression(NewIdentifierExpression("", "", "a"))
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetGroupBy(g)
assert.Nil(t, c.GroupBy())
@@ -369,12 +369,12 @@ func (ct *clausesTest) TestGroupBy() {
assert.Equal(t, g, c2.GroupBy())
}
-func (ct *clausesTest) TestSetGroupBy() {
- t := ct.T()
+func (sct *selectClausesTest) TestSetGroupBy() {
+ t := sct.T()
g := NewColumnListExpression(NewIdentifierExpression("", "", "a"))
g2 := NewColumnListExpression(NewIdentifierExpression("", "", "b"))
- c := NewClauses().SetGroupBy(g)
+ c := NewSelectClauses().SetGroupBy(g)
c2 := c.SetGroupBy(g2)
assert.Equal(t, g, c.GroupBy())
@@ -382,11 +382,11 @@ func (ct *clausesTest) TestSetGroupBy() {
assert.Equal(t, g2, c2.GroupBy())
}
-func (ct *clausesTest) TestLimit() {
- t := ct.T()
+func (sct *selectClausesTest) TestLimit() {
+ t := sct.T()
l := 1
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetLimit(l)
assert.Nil(t, c.Limit())
@@ -394,11 +394,11 @@ func (ct *clausesTest) TestLimit() {
assert.Equal(t, l, c2.Limit())
}
-func (ct *clausesTest) TestHasLimit() {
- t := ct.T()
+func (sct *selectClausesTest) TestHasLimit() {
+ t := sct.T()
l := 1
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetLimit(l)
assert.False(t, c.HasLimit())
@@ -406,11 +406,11 @@ func (ct *clausesTest) TestHasLimit() {
assert.True(t, c2.HasLimit())
}
-func (ct *clausesTest) TestCLearLimit() {
- t := ct.T()
+func (sct *selectClausesTest) TestCLearLimit() {
+ t := sct.T()
l := 1
- c := NewClauses().SetLimit(l)
+ c := NewSelectClauses().SetLimit(l)
c2 := c.ClearLimit()
assert.True(t, c.HasLimit())
@@ -418,12 +418,12 @@ func (ct *clausesTest) TestCLearLimit() {
assert.False(t, c2.HasLimit())
}
-func (ct *clausesTest) TestSetLimit() {
- t := ct.T()
+func (sct *selectClausesTest) TestSetLimit() {
+ t := sct.T()
l := 1
l2 := 2
- c := NewClauses().SetLimit(l)
+ c := NewSelectClauses().SetLimit(l)
c2 := c.SetLimit(2)
assert.Equal(t, l, c.Limit())
@@ -431,11 +431,11 @@ func (ct *clausesTest) TestSetLimit() {
assert.Equal(t, l2, c2.Limit())
}
-func (ct *clausesTest) TestOffset() {
- t := ct.T()
+func (sct *selectClausesTest) TestOffset() {
+ t := sct.T()
o := uint(1)
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetOffset(o)
assert.Equal(t, uint(0), c.Offset())
@@ -443,11 +443,11 @@ func (ct *clausesTest) TestOffset() {
assert.Equal(t, o, c2.Offset())
}
-func (ct *clausesTest) TestClearOffset() {
- t := ct.T()
+func (sct *selectClausesTest) TestClearOffset() {
+ t := sct.T()
o := uint(1)
- c := NewClauses().SetOffset(o)
+ c := NewSelectClauses().SetOffset(o)
c2 := c.ClearOffset()
assert.Equal(t, o, c.Offset())
@@ -455,12 +455,12 @@ func (ct *clausesTest) TestClearOffset() {
assert.Equal(t, uint(0), c2.Offset())
}
-func (ct *clausesTest) TestSetOffset() {
- t := ct.T()
+func (sct *selectClausesTest) TestSetOffset() {
+ t := sct.T()
o := uint(1)
o2 := uint(2)
- c := NewClauses().SetOffset(o)
+ c := NewSelectClauses().SetOffset(o)
c2 := c.SetOffset(2)
assert.Equal(t, o, c.Offset())
@@ -468,25 +468,25 @@ func (ct *clausesTest) TestSetOffset() {
assert.Equal(t, o2, c2.Offset())
}
-func (ct *clausesTest) TestCompounds() {
- t := ct.T()
+func (sct *selectClausesTest) TestCompounds() {
+ t := sct.T()
ce := NewCompoundExpression(UnionCompoundType, newTestAppendableExpression("SELECT * FROM foo", []interface{}{}))
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.CompoundsAppend(ce)
assert.Nil(t, c.Compounds())
assert.Equal(t, []CompoundExpression{ce}, c2.Compounds())
}
-func (ct *clausesTest) TestCompoundsAppend() {
- t := ct.T()
+func (sct *selectClausesTest) TestCompoundsAppend() {
+ t := sct.T()
ce := NewCompoundExpression(UnionCompoundType, newTestAppendableExpression("SELECT * FROM foo1", []interface{}{}))
ce2 := NewCompoundExpression(UnionCompoundType, newTestAppendableExpression("SELECT * FROM foo2", []interface{}{}))
- c := NewClauses().CompoundsAppend(ce)
+ c := NewSelectClauses().CompoundsAppend(ce)
c2 := c.CompoundsAppend(ce2)
assert.Equal(t, []CompoundExpression{ce}, c.Compounds())
@@ -494,12 +494,12 @@ func (ct *clausesTest) TestCompoundsAppend() {
assert.Equal(t, []CompoundExpression{ce, ce2}, c2.Compounds())
}
-func (ct *clausesTest) TestLock() {
- t := ct.T()
+func (sct *selectClausesTest) TestLock() {
+ t := sct.T()
l := NewLock(ForUpdate, Wait)
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.SetLock(l)
assert.Nil(t, c.Lock())
@@ -507,13 +507,13 @@ func (ct *clausesTest) TestLock() {
assert.Equal(t, l, c2.Lock())
}
-func (ct *clausesTest) TestSetLock() {
- t := ct.T()
+func (sct *selectClausesTest) TestSetLock() {
+ t := sct.T()
l := NewLock(ForUpdate, Wait)
l2 := NewLock(ForUpdate, NoWait)
- c := NewClauses().SetLock(l)
+ c := NewSelectClauses().SetLock(l)
c2 := c.SetLock(l2)
assert.Equal(t, l, c.Lock())
@@ -521,12 +521,12 @@ func (ct *clausesTest) TestSetLock() {
assert.Equal(t, l2, c2.Lock())
}
-func (ct *clausesTest) TestCommonTables() {
- t := ct.T()
+func (sct *selectClausesTest) TestCommonTables() {
+ t := sct.T()
cte := NewCommonTableExpression(true, "test", newTestAppendableExpression(`SELECT * FROM "foo"`, []interface{}{}))
- c := NewClauses()
+ c := NewSelectClauses()
c2 := c.CommonTablesAppend(cte)
assert.Nil(t, c.CommonTables())
@@ -534,13 +534,13 @@ func (ct *clausesTest) TestCommonTables() {
assert.Equal(t, []CommonTableExpression{cte}, c2.CommonTables())
}
-func (ct *clausesTest) TestAddCommonTablesAppend() {
- t := ct.T()
+func (sct *selectClausesTest) TestAddCommonTablesAppend() {
+ t := sct.T()
cte := NewCommonTableExpression(true, "test", testSQLExpression("test_cte"))
cte2 := NewCommonTableExpression(true, "test", testSQLExpression("test_cte2"))
- c := NewClauses().CommonTablesAppend(cte)
+ c := NewSelectClauses().CommonTablesAppend(cte)
c2 := c.CommonTablesAppend(cte2)
assert.Equal(t, []CommonTableExpression{cte}, c.CommonTables())
@@ -548,29 +548,29 @@ func (ct *clausesTest) TestAddCommonTablesAppend() {
assert.Equal(t, []CommonTableExpression{cte, cte2}, c2.CommonTables())
}
-func (ct *clausesTest) TestReturning() {
- t := ct.T()
-
- cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
-
- c := NewClauses()
- c2 := c.SetReturning(cl)
-
- assert.Nil(t, c.Returning())
-
- assert.Equal(t, cl, c2.Returning())
-}
-
-func (ct *clausesTest) TestSetReturning() {
- t := ct.T()
-
- cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
- cl2 := NewColumnListExpression(NewIdentifierExpression("", "", "col2"))
-
- c := NewClauses().SetReturning(cl)
- c2 := c.SetReturning(cl2)
-
- assert.Equal(t, cl, c.Returning())
-
- assert.Equal(t, cl2, c2.Returning())
-}
+// func (ct *selectClausesTest) TestReturning() {
+// t := ct.T()
+//
+// cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+//
+// c := NewSelectClauses()
+// c2 := c.SetReturning(cl)
+//
+// assert.Nil(t, c.Returning())
+//
+// assert.Equal(t, cl, c2.Returning())
+// }
+//
+// func (ct *selectClausesTest) TestSetReturning() {
+// t := ct.T()
+//
+// cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+// cl2 := NewColumnListExpression(NewIdentifierExpression("", "", "col2"))
+//
+// c := NewSelectClauses().SetReturning(cl)
+// c2 := c.SetReturning(cl2)
+//
+// assert.Equal(t, cl, c.Returning())
+//
+// assert.Equal(t, cl2, c2.Returning())
+// }
diff --git a/exp/truncate_clauses.go b/exp/truncate_clauses.go
new file mode 100644
index 00000000..2b3bd8b5
--- /dev/null
+++ b/exp/truncate_clauses.go
@@ -0,0 +1,50 @@
+package exp
+
+type (
+ TruncateClauses interface {
+ HasTable() bool
+ clone() *truncateClauses
+
+ Table() ColumnListExpression
+ SetTable(tables ColumnListExpression) TruncateClauses
+
+ Options() TruncateOptions
+ SetOptions(opts TruncateOptions) TruncateClauses
+ }
+ truncateClauses struct {
+ tables ColumnListExpression
+ options TruncateOptions
+ }
+)
+
+func NewTruncateClauses() TruncateClauses {
+ return &truncateClauses{}
+}
+
+func (tc *truncateClauses) HasTable() bool {
+ return tc.tables != nil
+}
+
+func (tc *truncateClauses) clone() *truncateClauses {
+ return &truncateClauses{
+ tables: tc.tables,
+ }
+}
+
+func (tc *truncateClauses) Table() ColumnListExpression {
+ return tc.tables
+}
+func (tc *truncateClauses) SetTable(tables ColumnListExpression) TruncateClauses {
+ ret := tc.clone()
+ ret.tables = tables
+ return ret
+}
+
+func (tc *truncateClauses) Options() TruncateOptions {
+ return tc.options
+}
+func (tc *truncateClauses) SetOptions(opts TruncateOptions) TruncateClauses {
+ ret := tc.clone()
+ ret.options = opts
+ return ret
+}
diff --git a/exp/truncate_clauses_test.go b/exp/truncate_clauses_test.go
new file mode 100644
index 00000000..6c56c4dc
--- /dev/null
+++ b/exp/truncate_clauses_test.go
@@ -0,0 +1,73 @@
+package exp
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+)
+
+type truncateClausesSuite struct {
+ suite.Suite
+}
+
+func TestTruncateClausesSuite(t *testing.T) {
+ suite.Run(t, new(truncateClausesSuite))
+}
+
+func (ucs *truncateClausesSuite) TestHasTable() {
+ t := ucs.T()
+ c := NewTruncateClauses()
+ cle := NewColumnListExpression("test1", "test2")
+ c2 := c.SetTable(cle)
+
+ assert.False(t, c.HasTable())
+
+ assert.True(t, c2.HasTable())
+}
+
+func (ucs *truncateClausesSuite) TestTable() {
+ t := ucs.T()
+ c := NewTruncateClauses()
+ cle := NewColumnListExpression("test1", "test2")
+ c2 := c.SetTable(cle)
+
+ assert.Nil(t, c.Table())
+
+ assert.Equal(t, cle, c2.Table())
+}
+
+func (ucs *truncateClausesSuite) TestSetTable() {
+ t := ucs.T()
+ cle := NewColumnListExpression("test1", "test2")
+ c := NewTruncateClauses().SetTable(cle)
+ cle2 := NewColumnListExpression("test3", "test4")
+ c2 := c.SetTable(cle2)
+
+ assert.Equal(t, cle, c.Table())
+
+ assert.Equal(t, cle2, c2.Table())
+}
+
+func (ucs *truncateClausesSuite) TestOptions() {
+ t := ucs.T()
+ c := NewTruncateClauses()
+ opts := TruncateOptions{Restrict: true, Identity: "RESTART", Cascade: true}
+ c2 := c.SetOptions(opts)
+
+ assert.Equal(t, TruncateOptions{}, c.Options())
+
+ assert.Equal(t, opts, c2.Options())
+}
+
+func (ucs *truncateClausesSuite) TestSetOptions() {
+ t := ucs.T()
+ opts := TruncateOptions{Restrict: true, Identity: "RESTART", Cascade: true}
+ c := NewTruncateClauses().SetOptions(opts)
+ opts2 := TruncateOptions{Restrict: false, Identity: "RESTART", Cascade: false}
+ c2 := c.SetOptions(opts2)
+
+ assert.Equal(t, opts, c.Options())
+
+ assert.Equal(t, opts2, c2.Options())
+}
diff --git a/exp/update.go b/exp/update.go
index 5975e906..626904ba 100644
--- a/exp/update.go
+++ b/exp/update.go
@@ -4,8 +4,8 @@ import (
"reflect"
"sort"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type (
diff --git a/exp/update_clauses.go b/exp/update_clauses.go
new file mode 100644
index 00000000..f98016c5
--- /dev/null
+++ b/exp/update_clauses.go
@@ -0,0 +1,189 @@
+package exp
+
+type (
+ UpdateClauses interface {
+ HasTable() bool
+ clone() *updateClauses
+
+ CommonTables() []CommonTableExpression
+ CommonTablesAppend(cte CommonTableExpression) UpdateClauses
+
+ Table() Expression
+ SetTable(table Expression) UpdateClauses
+
+ SetValues() interface{}
+ SetSetValues(values interface{}) UpdateClauses
+
+ Where() ExpressionList
+ ClearWhere() UpdateClauses
+ WhereAppend(expressions ...Expression) UpdateClauses
+
+ Order() ColumnListExpression
+ HasOrder() bool
+ ClearOrder() UpdateClauses
+ SetOrder(oes ...OrderedExpression) UpdateClauses
+ OrderAppend(...OrderedExpression) UpdateClauses
+ OrderPrepend(...OrderedExpression) UpdateClauses
+
+ Limit() interface{}
+ HasLimit() bool
+ ClearLimit() UpdateClauses
+ SetLimit(limit interface{}) UpdateClauses
+
+ Returning() ColumnListExpression
+ HasReturning() bool
+ SetReturning(cl ColumnListExpression) UpdateClauses
+ }
+ updateClauses struct {
+ commonTables []CommonTableExpression
+ table Expression
+ setValues interface{}
+ where ExpressionList
+ order ColumnListExpression
+ limit interface{}
+ returning ColumnListExpression
+ }
+)
+
+func NewUpdateClauses() UpdateClauses {
+ return &updateClauses{}
+}
+
+func (uc *updateClauses) HasTable() bool {
+ return uc.table != nil
+}
+
+func (uc *updateClauses) clone() *updateClauses {
+ return &updateClauses{
+ commonTables: uc.commonTables,
+ table: uc.table,
+ setValues: uc.setValues,
+ where: uc.where,
+ order: uc.order,
+ limit: uc.limit,
+ returning: uc.returning,
+ }
+}
+
+func (uc *updateClauses) CommonTables() []CommonTableExpression {
+ return uc.commonTables
+}
+func (uc *updateClauses) CommonTablesAppend(cte CommonTableExpression) UpdateClauses {
+ ret := uc.clone()
+ ret.commonTables = append(ret.commonTables, cte)
+ return ret
+}
+
+func (uc *updateClauses) Table() Expression {
+ return uc.table
+}
+func (uc *updateClauses) SetTable(table Expression) UpdateClauses {
+ ret := uc.clone()
+ ret.table = table
+ return ret
+}
+
+func (uc *updateClauses) SetValues() interface{} {
+ return uc.setValues
+}
+func (uc *updateClauses) SetSetValues(values interface{}) UpdateClauses {
+ ret := uc.clone()
+ ret.setValues = values
+ return ret
+}
+
+func (uc *updateClauses) Where() ExpressionList {
+ return uc.where
+}
+
+func (uc *updateClauses) ClearWhere() UpdateClauses {
+ ret := uc.clone()
+ ret.where = nil
+ return ret
+}
+
+func (uc *updateClauses) WhereAppend(expressions ...Expression) UpdateClauses {
+ expLen := len(expressions)
+ if expLen == 0 {
+ return uc
+ }
+ ret := uc.clone()
+ if ret.where == nil {
+ ret.where = NewExpressionList(AndType, expressions...)
+ } else {
+ ret.where = ret.where.Append(expressions...)
+ }
+ return ret
+}
+
+func (uc *updateClauses) Order() ColumnListExpression {
+ return uc.order
+}
+
+func (uc *updateClauses) HasOrder() bool {
+ return uc.order != nil
+}
+
+func (uc *updateClauses) ClearOrder() UpdateClauses {
+ ret := uc.clone()
+ ret.order = nil
+ return ret
+}
+
+func (uc *updateClauses) SetOrder(oes ...OrderedExpression) UpdateClauses {
+ ret := uc.clone()
+ ret.order = NewOrderedColumnList(oes...)
+ return ret
+}
+
+func (uc *updateClauses) OrderAppend(oes ...OrderedExpression) UpdateClauses {
+ if uc.order == nil {
+ return uc.SetOrder(oes...)
+ }
+ ret := uc.clone()
+ ret.order = ret.order.Append(NewOrderedColumnList(oes...).Columns()...)
+ return ret
+}
+
+func (uc *updateClauses) OrderPrepend(oes ...OrderedExpression) UpdateClauses {
+ if uc.order == nil {
+ return uc.SetOrder(oes...)
+ }
+ ret := uc.clone()
+ ret.order = NewOrderedColumnList(oes...).Append(ret.order.Columns()...)
+ return ret
+}
+
+func (uc *updateClauses) Limit() interface{} {
+ return uc.limit
+}
+
+func (uc *updateClauses) HasLimit() bool {
+ return uc.limit != nil
+}
+
+func (uc *updateClauses) ClearLimit() UpdateClauses {
+ ret := uc.clone()
+ ret.limit = nil
+ return ret
+}
+
+func (uc *updateClauses) SetLimit(limit interface{}) UpdateClauses {
+ ret := uc.clone()
+ ret.limit = limit
+ return ret
+}
+
+func (uc *updateClauses) Returning() ColumnListExpression {
+ return uc.returning
+}
+
+func (uc *updateClauses) HasReturning() bool {
+ return uc.returning != nil
+}
+
+func (uc *updateClauses) SetReturning(cl ColumnListExpression) UpdateClauses {
+ ret := uc.clone()
+ ret.returning = cl
+ return ret
+}
diff --git a/exp/update_clauses_test.go b/exp/update_clauses_test.go
new file mode 100644
index 00000000..4860cec9
--- /dev/null
+++ b/exp/update_clauses_test.go
@@ -0,0 +1,305 @@
+package exp
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+)
+
+type updateClausesSuite struct {
+ suite.Suite
+}
+
+func TestUpdateClausesSuite(t *testing.T) {
+ suite.Run(t, new(updateClausesSuite))
+}
+
+func (ucs *updateClausesSuite) TestHasTable() {
+ t := ucs.T()
+ c := NewUpdateClauses()
+ c2 := c.SetTable(NewIdentifierExpression("", "test", ""))
+
+ assert.False(t, c.HasTable())
+
+ assert.True(t, c2.HasTable())
+}
+
+func (ucs *updateClausesSuite) TestTable() {
+ t := ucs.T()
+ c := NewUpdateClauses()
+ ti := NewIdentifierExpression("", "a", "")
+ c2 := c.SetTable(ti)
+
+ assert.Nil(t, c.Table())
+
+ assert.Equal(t, ti, c2.Table())
+}
+
+func (ucs *updateClausesSuite) TestSetTable() {
+ t := ucs.T()
+ c := NewUpdateClauses()
+ ti := NewIdentifierExpression("", "a", "")
+ c2 := c.SetTable(ti)
+
+ assert.Nil(t, c.Table())
+
+ assert.Equal(t, ti, c2.Table())
+}
+
+func (ucs *updateClausesSuite) TestSetValues() {
+ t := ucs.T()
+ c := NewUpdateClauses()
+ r := Record{"a": "a1", "b": "b1"}
+ c2 := c.SetSetValues(r)
+
+ assert.Nil(t, c.SetValues())
+
+ assert.Equal(t, r, c2.SetValues())
+}
+
+func (ucs *updateClausesSuite) TestSetSetValues() {
+ t := ucs.T()
+ r := Record{"a": "a1", "b": "b1"}
+ c := NewUpdateClauses().SetSetValues(r)
+ r2 := Record{"a": "a2", "b": "b2"}
+ c2 := c.SetSetValues(r2)
+
+ assert.Equal(t, r, c.SetValues())
+
+ assert.Equal(t, r2, c2.SetValues())
+}
+
+func (ucs *updateClausesSuite) TestWhere() {
+ t := ucs.T()
+ w := Ex{"a": 1}
+
+ c := NewUpdateClauses()
+ c2 := c.WhereAppend(w)
+
+ assert.Nil(t, c.Where())
+
+ assert.Equal(t, NewExpressionList(AndType, w), c2.Where())
+}
+
+func (ucs *updateClausesSuite) TestClearWhere() {
+ t := ucs.T()
+ w := Ex{"a": 1}
+
+ c := NewUpdateClauses().WhereAppend(w)
+ c2 := c.ClearWhere()
+
+ assert.Equal(t, NewExpressionList(AndType, w), c.Where())
+
+ assert.Nil(t, c2.Where())
+}
+
+func (ucs *updateClausesSuite) TestWhereAppend() {
+ t := ucs.T()
+ w := Ex{"a": 1}
+ w2 := Ex{"b": 2}
+
+ c := NewUpdateClauses()
+ c2 := c.WhereAppend(w)
+
+ c3 := c.WhereAppend(w).WhereAppend(w2)
+
+ c4 := c.WhereAppend(w, w2)
+
+ assert.Nil(t, c.Where())
+
+ assert.Equal(t, NewExpressionList(AndType, w), c2.Where())
+ assert.Equal(t, NewExpressionList(AndType, w).Append(w2), c3.Where())
+ assert.Equal(t, NewExpressionList(AndType, w, w2), c4.Where())
+}
+
+func (ucs *updateClausesSuite) TestOrder() {
+ t := ucs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+
+ c := NewUpdateClauses()
+ c2 := c.SetOrder(oe)
+
+ assert.Nil(t, c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe), c2.Order())
+}
+
+func (ucs *updateClausesSuite) TestHasOrder() {
+ t := ucs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+
+ c := NewUpdateClauses()
+ c2 := c.SetOrder(oe)
+
+ assert.False(t, c.HasOrder())
+
+ assert.True(t, c2.HasOrder())
+}
+
+func (ucs *updateClausesSuite) TestClearOrder() {
+ t := ucs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+
+ c := NewUpdateClauses().SetOrder(oe)
+ c2 := c.ClearOrder()
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Nil(t, c2.Order())
+}
+
+func (ucs *updateClausesSuite) TestSetOrder() {
+ t := ucs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+ oe2 := NewIdentifierExpression("", "", "b").Desc()
+
+ c := NewUpdateClauses().SetOrder(oe)
+ c2 := c.SetOrder(oe2)
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe2), c2.Order())
+}
+
+func (ucs *updateClausesSuite) TestOrderAppend() {
+ t := ucs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+ oe2 := NewIdentifierExpression("", "", "b").Desc()
+
+ c := NewUpdateClauses().SetOrder(oe)
+ c2 := c.OrderAppend(oe2)
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe, oe2), c2.Order())
+}
+
+func (ucs *updateClausesSuite) TestOrderPrepend() {
+ t := ucs.T()
+ oe := NewIdentifierExpression("", "", "a").Desc()
+ oe2 := NewIdentifierExpression("", "", "b").Desc()
+
+ c := NewUpdateClauses().SetOrder(oe)
+ c2 := c.OrderPrepend(oe2)
+
+ assert.Equal(t, NewColumnListExpression(oe), c.Order())
+
+ assert.Equal(t, NewColumnListExpression(oe2, oe), c2.Order())
+}
+
+func (ucs *updateClausesSuite) TestLimit() {
+ t := ucs.T()
+ l := 1
+
+ c := NewUpdateClauses()
+ c2 := c.SetLimit(l)
+
+ assert.Nil(t, c.Limit())
+
+ assert.Equal(t, l, c2.Limit())
+}
+
+func (ucs *updateClausesSuite) TestHasLimit() {
+ t := ucs.T()
+ l := 1
+
+ c := NewUpdateClauses()
+ c2 := c.SetLimit(l)
+
+ assert.False(t, c.HasLimit())
+
+ assert.True(t, c2.HasLimit())
+}
+
+func (ucs *updateClausesSuite) TestCLearLimit() {
+ t := ucs.T()
+ l := 1
+
+ c := NewUpdateClauses().SetLimit(l)
+ c2 := c.ClearLimit()
+
+ assert.True(t, c.HasLimit())
+
+ assert.False(t, c2.HasLimit())
+}
+
+func (ucs *updateClausesSuite) TestSetLimit() {
+ t := ucs.T()
+ l := 1
+ l2 := 2
+
+ c := NewUpdateClauses().SetLimit(l)
+ c2 := c.SetLimit(2)
+
+ assert.Equal(t, l, c.Limit())
+
+ assert.Equal(t, l2, c2.Limit())
+}
+
+func (ucs *updateClausesSuite) TestCommonTables() {
+ t := ucs.T()
+
+ cte := NewCommonTableExpression(true, "test", newTestAppendableExpression(`SELECT * FROM "foo"`, []interface{}{}))
+
+ c := NewUpdateClauses()
+ c2 := c.CommonTablesAppend(cte)
+
+ assert.Nil(t, c.CommonTables())
+
+ assert.Equal(t, []CommonTableExpression{cte}, c2.CommonTables())
+}
+
+func (ucs *updateClausesSuite) TestAddCommonTablesAppend() {
+ t := ucs.T()
+
+ cte := NewCommonTableExpression(true, "test", testSQLExpression("test_cte"))
+ cte2 := NewCommonTableExpression(true, "test", testSQLExpression("test_cte2"))
+
+ c := NewUpdateClauses().CommonTablesAppend(cte)
+ c2 := c.CommonTablesAppend(cte2)
+
+ assert.Equal(t, []CommonTableExpression{cte}, c.CommonTables())
+
+ assert.Equal(t, []CommonTableExpression{cte, cte2}, c2.CommonTables())
+}
+
+func (ucs *updateClausesSuite) TestReturning() {
+ t := ucs.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+
+ c := NewUpdateClauses()
+ c2 := c.SetReturning(cl)
+
+ assert.Nil(t, c.Returning())
+
+ assert.Equal(t, cl, c2.Returning())
+}
+
+func (ucs *updateClausesSuite) TestHasReturning() {
+ t := ucs.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+
+ c := NewUpdateClauses()
+ c2 := c.SetReturning(cl)
+
+ assert.False(t, c.HasReturning())
+
+ assert.True(t, c2.HasReturning())
+}
+
+func (ucs *updateClausesSuite) TestSetReturning() {
+ t := ucs.T()
+
+ cl := NewColumnListExpression(NewIdentifierExpression("", "", "col"))
+ cl2 := NewColumnListExpression(NewIdentifierExpression("", "", "col2"))
+
+ c := NewUpdateClauses().SetReturning(cl)
+ c2 := c.SetReturning(cl2)
+
+ assert.Equal(t, cl, c.Returning())
+
+ assert.Equal(t, cl2, c2.Returning())
+}
diff --git a/expressions.go b/expressions.go
index ca75f0dc..b7a31b76 100644
--- a/expressions.go
+++ b/expressions.go
@@ -1,7 +1,7 @@
package goqu
import (
- "github.com/doug-martin/goqu/v7/exp"
+ "github.com/doug-martin/goqu/v8/exp"
)
type (
@@ -10,6 +10,7 @@ type (
ExOr = exp.ExOr
Op = exp.Op
Record = exp.Record
+ Vals = exp.Vals
// Options to use when generating a TRUNCATE statement
TruncateOptions = exp.TruncateOptions
)
diff --git a/expressions_example_test.go b/expressions_example_test.go
index 3d136b85..42b53c06 100644
--- a/expressions_example_test.go
+++ b/expressions_example_test.go
@@ -1,11 +1,12 @@
+// nolint:lll
package goqu_test
import (
"fmt"
"regexp"
- "github.com/doug-martin/goqu/v7"
- "github.com/doug-martin/goqu/v7/exp"
+ "github.com/doug-martin/goqu/v8"
+ "github.com/doug-martin/goqu/v8/exp"
)
func ExampleAVG() {
@@ -476,18 +477,18 @@ func ExampleDISTINCT_as() {
}
func ExampleDefault() {
- ds := goqu.From("items")
+ ds := goqu.Insert("items")
- sql, args, _ := ds.ToInsertSQL(goqu.Record{
+ sql, args, _ := ds.Rows(goqu.Record{
"name": goqu.Default(),
"address": goqu.Default(),
- })
+ }).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = ds.Prepared(true).ToInsertSQL(goqu.Record{
+ sql, args, _ = ds.Prepared(true).Rows(goqu.Record{
"name": goqu.Default(),
"address": goqu.Default(),
- })
+ }).ToSQL()
fmt.Println(sql, args)
// Output:
@@ -497,18 +498,18 @@ func ExampleDefault() {
}
func ExampleDoNothing() {
- ds := goqu.From("items")
+ ds := goqu.Insert("items")
- sql, args, _ := ds.ToInsertConflictSQL(goqu.DoNothing(), goqu.Record{
+ sql, args, _ := ds.Rows(goqu.Record{
"address": "111 Address",
"name": "bob",
- })
+ }).OnConflict(goqu.DoNothing()).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = ds.Prepared(true).ToInsertConflictSQL(goqu.DoNothing(), goqu.Record{
+ sql, args, _ = ds.Prepared(true).Rows(goqu.Record{
"address": "111 Address",
"name": "bob",
- })
+ }).OnConflict(goqu.DoNothing()).ToSQL()
fmt.Println(sql, args)
// Output:
@@ -518,18 +519,18 @@ func ExampleDoNothing() {
}
func ExampleDoUpdate() {
- ds := goqu.From("items")
+ ds := goqu.Insert("items")
- sql, args, _ := ds.ToInsertConflictSQL(
- goqu.DoUpdate("address", goqu.C("address").Set(goqu.I("EXCLUDED.address"))),
- goqu.Record{"address": "111 Address"},
- )
+ sql, args, _ := ds.
+ Rows(goqu.Record{"address": "111 Address"}).
+ OnConflict(goqu.DoUpdate("address", goqu.C("address").Set(goqu.I("EXCLUDED.address")))).
+ ToSQL()
fmt.Println(sql, args)
- sql, args, _ = ds.Prepared(true).ToInsertConflictSQL(
- goqu.DoUpdate("address", goqu.C("address").Set(goqu.I("EXCLUDED.address"))),
- goqu.Record{"address": "111 Address"},
- )
+ sql, args, _ = ds.Prepared(true).
+ Rows(goqu.Record{"address": "111 Address"}).
+ OnConflict(goqu.DoUpdate("address", goqu.C("address").Set(goqu.I("EXCLUDED.address")))).
+ ToSQL()
fmt.Println(sql, args)
// Output:
@@ -538,21 +539,26 @@ func ExampleDoUpdate() {
}
func ExampleDoUpdate_where() {
- ds := goqu.From("items")
+ ds := goqu.Insert("items")
- sql, args, _ := ds.ToInsertConflictSQL(
- goqu.DoUpdate("address", goqu.C("address").Set(goqu.I("EXCLUDED.address"))).Where(goqu.I("items.updated").IsNull()),
- goqu.Record{"address": "111 Address"},
- )
+ sql, args, _ := ds.
+ Rows(goqu.Record{"address": "111 Address"}).
+ OnConflict(goqu.DoUpdate(
+ "address",
+ goqu.C("address").Set(goqu.I("EXCLUDED.address"))).Where(goqu.I("items.updated").IsNull()),
+ ).
+ ToSQL()
fmt.Println(sql, args)
- sql, args, _ = ds.Prepared(true).ToInsertConflictSQL(
- goqu.DoUpdate("address", goqu.C("address").Set(goqu.I("EXCLUDED.address"))).Where(goqu.I("items.updated").IsNull()),
- goqu.Record{"address": "111 Address"},
- )
+ sql, args, _ = ds.Prepared(true).
+ Rows(goqu.Record{"address": "111 Address"}).
+ OnConflict(goqu.DoUpdate(
+ "address",
+ goqu.C("address").Set(goqu.I("EXCLUDED.address"))).Where(goqu.I("items.updated").IsNull()),
+ ).
+ ToSQL()
fmt.Println(sql, args)
- // nolint:lll
// Output:
// INSERT INTO "items" ("address") VALUES ('111 Address') ON CONFLICT (address) DO UPDATE SET "address"="EXCLUDED"."address" WHERE ("items"."updated" IS NULL) []
// INSERT INTO "items" ("address") VALUES (?) ON CONFLICT (address) DO UPDATE SET "address"="EXCLUDED"."address" WHERE ("items"."updated" IS NULL) [111 Address]
@@ -1225,6 +1231,7 @@ func ExampleUsing_withIdentifier() {
}
func ExampleEx() {
+
ds := goqu.From("items").Where(
goqu.Ex{
"col1": "a",
@@ -1242,7 +1249,6 @@ func ExampleEx() {
sql, args, _ = ds.Prepared(true).ToSQL()
fmt.Println(sql, args)
- // nolint:lll
// Output:
// SELECT * FROM "items" WHERE (("col1" = 'a') AND ("col2" = 1) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IS NULL) AND ("col6" IN ('a', 'b', 'c'))) []
// SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IS NULL) AND ("col6" IN (?, ?, ?))) [a 1 a b c]
@@ -1618,17 +1624,17 @@ func ExampleOp_withMultipleKeys() {
}
func ExampleRecord_insert() {
- ds := goqu.From("test")
+ ds := goqu.Insert("test")
records := []goqu.Record{
{"col1": 1, "col2": "foo"},
{"col1": 2, "col2": "bar"},
}
- sql, args, _ := ds.ToInsertSQL(records)
+ sql, args, _ := ds.Rows(records).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = ds.Prepared(true).ToInsertSQL(records)
+ sql, args, _ = ds.Prepared(true).Rows(records).ToSQL()
fmt.Println(sql, args)
// Output:
// INSERT INTO "test" ("col1", "col2") VALUES (1, 'foo'), (2, 'bar') []
@@ -1636,15 +1642,30 @@ func ExampleRecord_insert() {
}
func ExampleRecord_update() {
- ds := goqu.From("test")
+ ds := goqu.Update("test")
update := goqu.Record{"col1": 1, "col2": "foo"}
- sql, args, _ := ds.ToUpdateSQL(update)
+ sql, args, _ := ds.Set(update).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = ds.Prepared(true).ToUpdateSQL(update)
+ sql, args, _ = ds.Prepared(true).Set(update).ToSQL()
fmt.Println(sql, args)
// Output:
// UPDATE "test" SET "col1"=1,"col2"='foo' []
// UPDATE "test" SET "col1"=?,"col2"=? [1 foo]
}
+
+func ExampleVals() {
+ ds := goqu.Insert("user").
+ Cols("first_name", "last_name", "is_verified").
+ Vals(
+ goqu.Vals{"Greg", "Farley", true},
+ goqu.Vals{"Jimmy", "Stewart", true},
+ goqu.Vals{"Jeff", "Jeffers", false},
+ )
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name", "is_verified") VALUES ('Greg', 'Farley', TRUE), ('Jimmy', 'Stewart', TRUE), ('Jeff', 'Jeffers', FALSE) []
+}
diff --git a/go.mod b/go.mod
index c847088e..d1e08e02 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module github.com/doug-martin/goqu/v7
+module github.com/doug-martin/goqu/v8
go 1.12
diff --git a/goqu.go b/goqu.go
index 6b091069..611e0db5 100644
--- a/goqu.go
+++ b/goqu.go
@@ -13,7 +13,7 @@ Please see https://github.com/doug-martin/goqu for an introduction to goqu.
package goqu
import (
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type DialectWrapper struct {
@@ -25,10 +25,36 @@ func Dialect(dialect string) DialectWrapper {
return DialectWrapper{dialect: dialect}
}
-func (dw DialectWrapper) From(table ...interface{}) *Dataset {
+// Create a new dataset for creating SELECT sql statements
+func (dw DialectWrapper) From(table ...interface{}) *SelectDataset {
return From(table...).WithDialect(dw.dialect)
}
+// Create a new dataset for creating SELECT sql statements
+func (dw DialectWrapper) Select(cols ...interface{}) *SelectDataset {
+ return newDataset(dw.dialect, nil).Select(cols...)
+}
+
+// Create a new dataset for creating UPDATE sql statements
+func (dw DialectWrapper) Update(table interface{}) *UpdateDataset {
+ return Update(table).WithDialect(dw.dialect)
+}
+
+// Create a new dataset for creating INSERT sql statements
+func (dw DialectWrapper) Insert(table interface{}) *InsertDataset {
+ return Insert(table).WithDialect(dw.dialect)
+}
+
+// Create a new dataset for creating DELETE sql statements
+func (dw DialectWrapper) Delete(table interface{}) *DeleteDataset {
+ return Delete(table).WithDialect(dw.dialect)
+}
+
+// Create a new dataset for creating TRUNCATE sql statements
+func (dw DialectWrapper) Truncate(table ...interface{}) *TruncateDataset {
+ return Truncate(table...).WithDialect(dw.dialect)
+}
+
func (dw DialectWrapper) DB(db SQLDatabase) *Database {
return newDatabase(dw.dialect, db)
}
diff --git a/goqu_example_test.go b/goqu_example_test.go
index 4c02f5c2..e78ef6eb 100644
--- a/goqu_example_test.go
+++ b/goqu_example_test.go
@@ -4,15 +4,15 @@ import (
"fmt"
"github.com/DATA-DOG/go-sqlmock"
- "github.com/doug-martin/goqu/v7"
- _ "github.com/doug-martin/goqu/v7/dialect/mysql"
- _ "github.com/doug-martin/goqu/v7/dialect/postgres"
- _ "github.com/doug-martin/goqu/v7/dialect/sqlite3"
+ "github.com/doug-martin/goqu/v8"
+ _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+ _ "github.com/doug-martin/goqu/v8/dialect/postgres"
+ _ "github.com/doug-martin/goqu/v8/dialect/sqlite3"
)
// Creating a mysql dataset. Be sure to import the mysql adapter
func ExampleDialect_datasetMysql() {
- // import _ "github.com/doug-martin/goqu/v7/adapters/mysql"
+ // import _ "github.com/doug-martin/goqu/v8/adapters/mysql"
d := goqu.Dialect("mysql")
ds := d.From("test").Where(goqu.Ex{
@@ -33,7 +33,7 @@ func ExampleDialect_datasetMysql() {
// Creating a mysql database. Be sure to import the mysql adapter
func ExampleDialect_dbMysql() {
- // import _ "github.com/doug-martin/goqu/v7/adapters/mysql"
+ // import _ "github.com/doug-martin/goqu/v8/adapters/mysql"
type item struct {
ID int64 `db:"id"`
@@ -83,7 +83,7 @@ func ExampleDialect_dbMysql() {
// Creating a mysql dataset. Be sure to import the postgres adapter
func ExampleDialect_datasetPostgres() {
- // import _ "github.com/doug-martin/goqu/v7/adapters/postgres"
+ // import _ "github.com/doug-martin/goqu/v8/adapters/postgres"
d := goqu.Dialect("postgres")
ds := d.From("test").Where(goqu.Ex{
@@ -104,7 +104,7 @@ func ExampleDialect_datasetPostgres() {
// Creating a postgres dataset. Be sure to import the postgres adapter
func ExampleDialect_dbPostgres() {
- // import _ "github.com/doug-martin/goqu/v7/adapters/postgres"
+ // import _ "github.com/doug-martin/goqu/v8/adapters/postgres"
type item struct {
ID int64 `db:"id"`
@@ -154,7 +154,7 @@ func ExampleDialect_dbPostgres() {
// Creating a mysql dataset. Be sure to import the sqlite3 adapter
func ExampleDialect_datasetSqlite3() {
- // import _ "github.com/doug-martin/goqu/v7/adapters/sqlite3"
+ // import _ "github.com/doug-martin/goqu/v8/adapters/sqlite3"
d := goqu.Dialect("sqlite3")
ds := d.From("test").Where(goqu.Ex{
@@ -175,7 +175,7 @@ func ExampleDialect_datasetSqlite3() {
// Creating a sqlite3 database. Be sure to import the sqlite3 adapter
func ExampleDialect_dbSqlite3() {
- // import _ "github.com/doug-martin/goqu/v7/adapters/sqlite3"
+ // import _ "github.com/doug-martin/goqu/v8/adapters/sqlite3"
type item struct {
ID int64 `db:"id"`
@@ -222,11 +222,3 @@ func ExampleDialect_dbSqlite3() {
// {1 111 Test Addr Test1} true
// {1 111 Test Addr Test1} true
}
-
-func ExampleFrom() {
- sql, args, _ := goqu.From("test").ToSQL()
- fmt.Println(sql, args)
-
- // Output:
- // SELECT * FROM "test" []
-}
diff --git a/insert_dataset.go b/insert_dataset.go
new file mode 100644
index 00000000..5f7235a1
--- /dev/null
+++ b/insert_dataset.go
@@ -0,0 +1,211 @@
+package goqu
+
+import (
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+)
+
+type InsertDataset struct {
+ dialect SQLDialect
+ clauses exp.InsertClauses
+ isPrepared bool
+ queryFactory exec.QueryFactory
+}
+
+// used internally by database to create a database with a specific adapter
+func newInsertDataset(d string, queryFactory exec.QueryFactory) *InsertDataset {
+ return &InsertDataset{
+ clauses: exp.NewInsertClauses(),
+ dialect: GetDialect(d),
+ queryFactory: queryFactory,
+ }
+}
+
+// Creates a new InsertDataset for the provided table. Using this method will only allow you
+// to create SQL user Database#From to create an InsertDataset with query capabilities
+func Insert(table interface{}) *InsertDataset {
+ return newInsertDataset("default", nil).Into(table)
+}
+
+// Set the parameter interpolation behavior. See examples
+//
+// 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
+ return ret
+}
+
+func (id *InsertDataset) IsPrepared() bool {
+ return id.isPrepared
+}
+
+// Sets the adapter used to serialize values and create the SQL statement
+func (id *InsertDataset) WithDialect(dl string) *InsertDataset {
+ ds := id.copy(id.GetClauses())
+ ds.dialect = GetDialect(dl)
+ return id
+}
+
+// Returns the current adapter on the dataset
+func (id *InsertDataset) Dialect() SQLDialect {
+ return id.dialect
+}
+
+// Returns the current adapter on the dataset
+func (id *InsertDataset) SetDialect(dialect SQLDialect) *InsertDataset {
+ cd := id.copy(id.GetClauses())
+ cd.dialect = dialect
+ return cd
+}
+
+func (id *InsertDataset) Expression() exp.Expression {
+ return id
+}
+
+// Clones the dataset
+func (id *InsertDataset) Clone() exp.Expression {
+ return id.copy(id.clauses)
+}
+
+// Returns the current clauses on the dataset.
+func (id *InsertDataset) GetClauses() exp.InsertClauses {
+ return id.clauses
+}
+
+// used interally to copy the dataset
+func (id *InsertDataset) copy(clauses exp.InsertClauses) *InsertDataset {
+ return &InsertDataset{
+ dialect: id.dialect,
+ clauses: clauses,
+ isPrepared: id.isPrepared,
+ queryFactory: id.queryFactory,
+ }
+}
+
+// Creates a WITH clause for a common table expression (CTE).
+//
+// The name will be available to SELECT from in the associated query; and can optionally
+// contain a list of column names "name(col1, col2, col3)".
+//
+// The name will refer to the results of the specified subquery.
+func (id *InsertDataset) With(name string, subquery exp.Expression) *InsertDataset {
+ return id.copy(id.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
+}
+
+// Creates a WITH RECURSIVE clause for a common table expression (CTE)
+//
+// The name will be available to SELECT from in the associated query; and must
+// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
+//
+// The name will refer to the results of the specified subquery. The subquery for
+// a recursive query will always end with a UNION or UNION ALL with a clause that
+// refers to the CTE by name.
+func (id *InsertDataset) WithRecursive(name string, subquery exp.Expression) *InsertDataset {
+ return id.copy(id.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
+}
+
+// Sets the table to insert INTO. This return a new dataset with the original table replaced. See examples.
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// Expression: Any valid expression (IdentifierExpression, AliasedExpression, Literal, etc.)
+func (id *InsertDataset) Into(into interface{}) *InsertDataset {
+ switch t := into.(type) {
+ case exp.Expression:
+ return id.copy(id.clauses.SetInto(t))
+ case string:
+ return id.copy(id.clauses.SetInto(exp.ParseIdentifier(t)))
+ default:
+ panic("unsupported table type, a string or identifier expression is required")
+ }
+}
+
+// Sets the Columns to insert into
+func (id *InsertDataset) Cols(cols ...interface{}) *InsertDataset {
+ return id.copy(id.clauses.SetCols(exp.NewColumnListExpression(cols...)))
+}
+
+// Clears the Columns to insert into
+func (id *InsertDataset) ClearCols() *InsertDataset {
+ return id.copy(id.clauses.SetCols(exp.NewColumnListExpression(exp.Star())))
+}
+
+// Adds columns to the current list of columns clause. See examples
+func (id *InsertDataset) ColsAppend(cols ...interface{}) *InsertDataset {
+ return id.copy(id.clauses.ColsAppend(exp.NewColumnListExpression(cols...)))
+}
+
+// Adds a subquery to the insert. See examples.
+func (id *InsertDataset) FromQuery(from exp.AppendableExpression) *InsertDataset {
+ return id.copy(id.clauses.SetFrom(from))
+}
+
+// Manually set values to insert See examples.
+func (id *InsertDataset) Vals(vals ...[]interface{}) *InsertDataset {
+ return id.copy(id.clauses.ValsAppend(vals))
+}
+
+// Clears the values. See examples.
+func (id *InsertDataset) ClearVals() *InsertDataset {
+ return id.copy(id.clauses.SetVals(nil))
+}
+
+// Insert rows. Rows can be a map, goqu.Record or struct. See examples.
+func (id *InsertDataset) Rows(rows ...interface{}) *InsertDataset {
+ return id.copy(id.clauses.SetRows(rows))
+}
+
+// Clears the rows for this insert dataset. See examples.
+func (id *InsertDataset) ClearRows() *InsertDataset {
+ return id.copy(id.clauses.SetRows(nil))
+}
+
+// Adds a RETURNING clause to the dataset if the adapter supports it See examples.
+func (id *InsertDataset) Returning(returning ...interface{}) *InsertDataset {
+ return id.copy(id.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
+}
+
+// Adds an (ON CONFLICT/ON DUPLICATE KEY) clause to the dataset if the dialect supports it. See examples.
+func (id *InsertDataset) OnConflict(conflict exp.ConflictExpression) *InsertDataset {
+ return id.copy(id.clauses.SetOnConflict(conflict))
+}
+
+// Clears the on conflict clause. See example
+func (id *InsertDataset) ClearOnConflict() *InsertDataset {
+ return id.OnConflict(nil)
+}
+
+// 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`
+// type Item struct{
+// Id uint32 `db:"id" goqu:"skipinsert"`
+// Name string `db:"name"`
+// }
+//
+// rows: variable number arguments of either map[string]interface, Record, struct, or a single slice argument of the
+// accepted types.
+//
+// Errors:
+// * There is no INTO clause
+// * Different row types passed in, all rows must be of the same type
+// * Maps with different numbers of K/V pairs
+// * Rows of different lengths, (i.e. (Record{"name": "a"}, Record{"name": "a", "age": 10})
+// * Error generating SQL
+func (id *InsertDataset) ToSQL() (sql string, params []interface{}, err error) {
+ return id.insertSQLBuilder().ToSQL()
+}
+
+// Generates the INSERT sql, and returns an QueryExecutor struct with the sql set to the INSERT statement
+// db.Insert("test").Rows(Record{"name":"Bob"}).Executor().Exec()
+//
+func (id *InsertDataset) Executor() exec.QueryExecutor {
+ return id.queryFactory.FromSQLBuilder(id.insertSQLBuilder())
+}
+
+func (id *InsertDataset) insertSQLBuilder() sb.SQLBuilder {
+ buf := sb.NewSQLBuilder(id.isPrepared)
+ id.dialect.ToInsertSQL(buf, id.clauses)
+ return buf
+}
diff --git a/insert_dataset_example_test.go b/insert_dataset_example_test.go
new file mode 100644
index 00000000..e41fc33f
--- /dev/null
+++ b/insert_dataset_example_test.go
@@ -0,0 +1,622 @@
+// nolint:lll
+package goqu_test
+
+import (
+ "database/sql"
+ "fmt"
+ "time"
+
+ "github.com/doug-martin/goqu/v8"
+ _ "github.com/doug-martin/goqu/v8/dialect/postgres"
+)
+
+func ExampleInsert_goquRecord() {
+ ds := goqu.Insert("user").Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+ goqu.Record{"first_name": "Jimmy", "last_name": "Stewart"},
+ goqu.Record{"first_name": "Jeff", "last_name": "Jeffers"},
+ )
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+}
+
+func ExampleInsert_map() {
+ ds := goqu.Insert("user").Rows(
+ map[string]interface{}{"first_name": "Greg", "last_name": "Farley"},
+ map[string]interface{}{"first_name": "Jimmy", "last_name": "Stewart"},
+ map[string]interface{}{"first_name": "Jeff", "last_name": "Jeffers"},
+ )
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+}
+
+func ExampleInsert_struct() {
+ type User struct {
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+ }
+ ds := goqu.Insert("user").Rows(
+ User{FirstName: "Greg", LastName: "Farley"},
+ User{FirstName: "Jimmy", LastName: "Stewart"},
+ User{FirstName: "Jeff", LastName: "Jeffers"},
+ )
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+}
+
+func ExampleInsert_prepared() {
+ ds := goqu.Insert("user").Prepared(true).Rows(
+ goqu.Record{"first_name": "Greg", "last_name": "Farley"},
+ goqu.Record{"first_name": "Jimmy", "last_name": "Stewart"},
+ goqu.Record{"first_name": "Jeff", "last_name": "Jeffers"},
+ )
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name") VALUES (?, ?), (?, ?), (?, ?) [Greg Farley Jimmy Stewart Jeff Jeffers]
+}
+
+func ExampleInsert_fromQuery() {
+ ds := goqu.Insert("user").Prepared(true).
+ FromQuery(goqu.From("other_table"))
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" SELECT * FROM "other_table" []
+}
+
+func ExampleInsert_fromQueryWithCols() {
+ ds := goqu.Insert("user").Prepared(true).
+ Cols("first_name", "last_name").
+ FromQuery(goqu.From("other_table").Select("fn", "ln"))
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name") SELECT "fn", "ln" FROM "other_table" []
+}
+
+func ExampleInsert_colsAndVals() {
+ ds := goqu.Insert("user").
+ Cols("first_name", "last_name").
+ Vals(
+ goqu.Vals{"Greg", "Farley"},
+ goqu.Vals{"Jimmy", "Stewart"},
+ goqu.Vals{"Jeff", "Jeffers"},
+ )
+ insertSQL, args, _ := ds.ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "user" ("first_name", "last_name") VALUES ('Greg', 'Farley'), ('Jimmy', 'Stewart'), ('Jeff', 'Jeffers') []
+}
+
+func ExampleInsertDataset_Executor_withRecord() {
+ db := getDb()
+ insert := db.Insert("goqu_user").Rows(
+ goqu.Record{"first_name": "Jed", "last_name": "Riley", "created": time.Now()},
+ ).Executor()
+ if _, err := insert.Exec(); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Println("Inserted 1 user")
+ }
+
+ users := []goqu.Record{
+ {"first_name": "Greg", "last_name": "Farley", "created": time.Now()},
+ {"first_name": "Jimmy", "last_name": "Stewart", "created": time.Now()},
+ {"first_name": "Jeff", "last_name": "Jeffers", "created": time.Now()},
+ }
+ if _, err := db.Insert("goqu_user").Rows(users).Executor().Exec(); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Printf("Inserted %d users", len(users))
+ }
+
+ // Output:
+ // Inserted 1 user
+ // Inserted 3 users
+}
+
+func ExampleInsertDataset_Executor_recordReturning() {
+ db := getDb()
+
+ type User struct {
+ ID sql.NullInt64 `db:"id"`
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+ Created time.Time `db:"created"`
+ }
+
+ insert := db.Insert("goqu_user").Returning(goqu.C("id")).Rows(
+ goqu.Record{"first_name": "Jed", "last_name": "Riley", "created": time.Now()},
+ ).Executor()
+ var id int64
+ if _, err := insert.ScanVal(&id); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Printf("Inserted 1 user id:=%d\n", id)
+ }
+
+ insert = db.Insert("goqu_user").Returning(goqu.Star()).Rows([]goqu.Record{
+ {"first_name": "Greg", "last_name": "Farley", "created": time.Now()},
+ {"first_name": "Jimmy", "last_name": "Stewart", "created": time.Now()},
+ {"first_name": "Jeff", "last_name": "Jeffers", "created": time.Now()},
+ }).Executor()
+ var insertedUsers []User
+ if err := insert.ScanStructs(&insertedUsers); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ for _, u := range insertedUsers {
+ fmt.Printf("Inserted user: [ID=%d], [FirstName=%+s] [LastName=%s]\n", u.ID.Int64, u.FirstName, u.LastName)
+ }
+
+ }
+
+ // Output:
+ // Inserted 1 user id:=5
+ // Inserted user: [ID=6], [FirstName=Greg] [LastName=Farley]
+ // Inserted user: [ID=7], [FirstName=Jimmy] [LastName=Stewart]
+ // Inserted user: [ID=8], [FirstName=Jeff] [LastName=Jeffers]
+}
+
+func ExampleInsertDataset_Executor_scanStructs() {
+ db := getDb()
+
+ type User struct {
+ ID sql.NullInt64 `db:"id" goqu:"skipinsert"`
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+ Created time.Time `db:"created"`
+ }
+
+ insert := db.Insert("goqu_user").Returning("id").Rows(
+ User{FirstName: "Jed", LastName: "Riley"},
+ ).Executor()
+ var id int64
+ if _, err := insert.ScanVal(&id); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Printf("Inserted 1 user id:=%d\n", id)
+ }
+
+ insert = db.Insert("goqu_user").Returning(goqu.Star()).Rows([]User{
+ {FirstName: "Greg", LastName: "Farley", Created: time.Now()},
+ {FirstName: "Jimmy", LastName: "Stewart", Created: time.Now()},
+ {FirstName: "Jeff", LastName: "Jeffers", Created: time.Now()},
+ }).Executor()
+ var insertedUsers []User
+ if err := insert.ScanStructs(&insertedUsers); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ for _, u := range insertedUsers {
+ fmt.Printf("Inserted user: [ID=%d], [FirstName=%+s] [LastName=%s]\n", u.ID.Int64, u.FirstName, u.LastName)
+ }
+
+ }
+
+ // Output:
+ // Inserted 1 user id:=5
+ // Inserted user: [ID=6], [FirstName=Greg] [LastName=Farley]
+ // Inserted user: [ID=7], [FirstName=Jimmy] [LastName=Stewart]
+ // Inserted user: [ID=8], [FirstName=Jeff] [LastName=Jeffers]
+}
+
+func ExampleInsertDataset_FromQuery() {
+ insertSQL, _, _ := goqu.Insert("test").
+ FromQuery(goqu.From("test2").Where(goqu.C("age").Gt(10))).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" SELECT * FROM "test2" WHERE ("age" > 10)
+}
+
+func ExampleInsertDataset_ToSQL() {
+ type item struct {
+ ID uint32 `db:"id" goqu:"skipinsert"`
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ insertSQL, args, _ := goqu.Insert("items").Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").Rows(
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+ ).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").Rows(
+ []item{
+ {Name: "Test1", Address: "111 Test Addr"},
+ {Name: "Test2", Address: "112 Test Addr"},
+ }).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.From("items").Insert().Rows(
+ []goqu.Record{
+ {"name": "Test1", "address": "111 Test Addr"},
+ {"name": "Test2", "address": "112 Test Addr"},
+ }).ToSQL()
+ fmt.Println(insertSQL, args)
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+}
+
+func ExampleInsertDataset_Prepared() {
+ type item struct {
+ ID uint32 `db:"id" goqu:"skipinsert"`
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+
+ insertSQL, args, _ := goqu.Insert("items").Prepared(true).Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").Prepared(true).Rows(
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+ ).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").Prepared(true).Rows(
+ []item{
+ {Name: "Test1", Address: "111 Test Addr"},
+ {Name: "Test2", Address: "112 Test Addr"},
+ }).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").Prepared(true).Rows(
+ []goqu.Record{
+ {"name": "Test1", "address": "111 Test Addr"},
+ {"name": "Test2", "address": "112 Test Addr"},
+ }).ToSQL()
+ fmt.Println(insertSQL, args)
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
+ // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
+ // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
+ // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
+}
+
+func ExampleInsertDataset_ClearRows() {
+ type item struct {
+ ID uint32 `goqu:"skipinsert"`
+ Address string
+ Name string
+ }
+ ds := goqu.Insert("items").Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ )
+ insertSQL, args, _ := ds.ClearRows().ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" DEFAULT VALUES []
+}
+
+func ExampleInsertDataset_Rows_withNoDbTag() {
+ type item struct {
+ ID uint32 `goqu:"skipinsert"`
+ Address string
+ Name string
+ }
+ insertSQL, args, _ := goqu.Insert("items").
+ Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").
+ Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").
+ Rows([]item{
+ {Name: "Test1", Address: "111 Test Addr"},
+ {Name: "Test2", Address: "112 Test Addr"},
+ }).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+}
+
+func ExampleInsertDataset_Rows_withGoquSkipInsertTag() {
+ type item struct {
+ ID uint32 `goqu:"skipinsert"`
+ Address string
+ Name string `goqu:"skipinsert"`
+ }
+ insertSQL, args, _ := goqu.Insert("items").
+ Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").
+ Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ insertSQL, args, _ = goqu.Insert("items").
+ Rows([]item{
+ {Name: "Test1", Address: "111 Test Addr"},
+ {Name: "Test2", Address: "112 Test Addr"},
+ }).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') []
+ // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') []
+ // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') []
+}
+
+func ExampleInsertDataset_ClearOnConflict() {
+ type item struct {
+ ID uint32 `db:"id" goqu:"skipinsert"`
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ ds := goqu.Insert("items").OnConflict(goqu.DoNothing())
+ insertSQL, args, _ := ds.ClearOnConflict().Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
+}
+
+func ExampleInsertDataset_OnConflict_doNothing() {
+ type item struct {
+ ID uint32 `db:"id" goqu:"skipinsert"`
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ insertSQL, args, _ := goqu.Insert("items").Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "112 Test Addr"},
+ ).OnConflict(goqu.DoNothing()).ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
+}
+
+func ExampleInsertDataset_OnConflict_doUpdate() {
+ insertSQL, args, _ := goqu.Insert("items").
+ Rows(
+ goqu.Record{"name": "Test1", "address": "111 Test Addr"},
+ goqu.Record{"name": "Test2", "address": "112 Test Addr"},
+ ).
+ OnConflict(goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")})).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() []
+}
+
+func ExampleInsertDataset_OnConflict_doUpdateWithWhere() {
+ type item struct {
+ ID uint32 `db:"id" goqu:"skipinsert"`
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ insertSQL, args, _ := goqu.Insert("items").
+ Rows([]item{
+ {Name: "Test1", Address: "111 Test Addr"},
+ {Name: "Test2", Address: "112 Test Addr"},
+ }).
+ OnConflict(goqu.DoUpdate(
+ "key",
+ goqu.Record{"updated": goqu.L("NOW()")}).Where(goqu.C("allow_update").IsTrue()),
+ ).
+ ToSQL()
+ fmt.Println(insertSQL, args)
+
+ // Output:
+ // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() WHERE ("allow_update" IS TRUE) []
+}
+
+func ExampleInsertDataset_Returning() {
+ insertSQL, _, _ := goqu.Insert("test").
+ Returning("id").
+ Rows(goqu.Record{"a": "a", "b": "b"}).
+ ToSQL()
+ fmt.Println(insertSQL)
+ insertSQL, _, _ = goqu.Insert("test").
+ Returning(goqu.T("test").All()).
+ Rows(goqu.Record{"a": "a", "b": "b"}).
+ ToSQL()
+ fmt.Println(insertSQL)
+ insertSQL, _, _ = goqu.Insert("test").
+ Returning("a", "b").
+ Rows(goqu.Record{"a": "a", "b": "b"}).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "id"
+ // INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "test".*
+ // INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "a", "b"
+}
+
+func ExampleInsertDataset_With() {
+ insertSQL, _, _ := goqu.Insert("foo").
+ With("other", goqu.From("bar").Where(goqu.C("id").Gt(10))).
+ FromQuery(goqu.From("other")).
+ ToSQL()
+ fmt.Println(insertSQL)
+
+ // Output:
+ // WITH other AS (SELECT * FROM "bar" WHERE ("id" > 10)) INSERT INTO "foo" SELECT * FROM "other"
+}
+
+func ExampleInsertDataset_WithRecursive() {
+ insertSQL, _, _ := goqu.Insert("num_count").
+ WithRecursive("nums(x)",
+ goqu.From().Select(goqu.L("1")).
+ UnionAll(goqu.From("nums").
+ Select(goqu.L("x+1")).Where(goqu.C("x").Lt(5))),
+ ).
+ FromQuery(goqu.From("nums")).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // WITH RECURSIVE nums(x) AS (SELECT 1 UNION ALL (SELECT x+1 FROM "nums" WHERE ("x" < 5))) INSERT INTO "num_count" SELECT * FROM "nums"
+}
+
+func ExampleInsertDataset_Into() {
+ ds := goqu.Insert("test")
+ insertSQL, _, _ := ds.Into("test2").Rows(goqu.Record{"first_name": "bob", "last_name": "yukon"}).ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test2" ("first_name", "last_name") VALUES ('bob', 'yukon')
+}
+
+func ExampleInsertDataset_Into_aliased() {
+ ds := goqu.Insert("test")
+ insertSQL, _, _ := ds.
+ Into(goqu.T("test").As("t")).
+ Rows(goqu.Record{"first_name": "bob", "last_name": "yukon"}).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" AS "t" ("first_name", "last_name") VALUES ('bob', 'yukon')
+}
+
+func ExampleInsertDataset_Cols() {
+ insertSQL, _, _ := goqu.Insert("test").
+ Cols("a", "b", "c").
+ Vals(
+ []interface{}{"a1", "b1", "c1"},
+ []interface{}{"a2", "b1", "c1"},
+ []interface{}{"a3", "b1", "c1"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" ("a", "b", "c") VALUES ('a1', 'b1', 'c1'), ('a2', 'b1', 'c1'), ('a3', 'b1', 'c1')
+}
+
+func ExampleInsertDataset_Cols_withFromQuery() {
+ insertSQL, _, _ := goqu.Insert("test").
+ Cols("a", "b", "c").
+ FromQuery(goqu.From("foo").Select("d", "e", "f")).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" ("a", "b", "c") SELECT "d", "e", "f" FROM "foo"
+}
+
+func ExampleInsertDataset_ColsAppend() {
+ insertSQL, _, _ := goqu.Insert("test").
+ Cols("a", "b").
+ ColsAppend("c").
+ Vals(
+ []interface{}{"a1", "b1", "c1"},
+ []interface{}{"a2", "b1", "c1"},
+ []interface{}{"a3", "b1", "c1"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" ("a", "b", "c") VALUES ('a1', 'b1', 'c1'), ('a2', 'b1', 'c1'), ('a3', 'b1', 'c1')
+}
+
+func ExampleInsertDataset_ClearCols() {
+ ds := goqu.Insert("test").Cols("a", "b", "c")
+ insertSQL, _, _ := ds.ClearCols().Cols("other_a", "other_b", "other_c").
+ FromQuery(goqu.From("foo").Select("d", "e", "f")).
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" ("other_a", "other_b", "other_c") SELECT "d", "e", "f" FROM "foo"
+}
+
+func ExampleInsertDataset_Vals() {
+ insertSQL, _, _ := goqu.Insert("test").
+ Cols("a", "b", "c").
+ Vals(
+ []interface{}{"a1", "b1", "c1"},
+ []interface{}{"a2", "b2", "c2"},
+ []interface{}{"a3", "b3", "c3"},
+ ).
+ ToSQL()
+ fmt.Println(insertSQL)
+
+ insertSQL, _, _ = goqu.Insert("test").
+ Cols("a", "b", "c").
+ Vals([]interface{}{"a1", "b1", "c1"}).
+ Vals([]interface{}{"a2", "b2", "c2"}).
+ Vals([]interface{}{"a3", "b3", "c3"}).
+ ToSQL()
+ fmt.Println(insertSQL)
+
+ // Output:
+ // INSERT INTO "test" ("a", "b", "c") VALUES ('a1', 'b1', 'c1'), ('a2', 'b2', 'c2'), ('a3', 'b3', 'c3')
+ // INSERT INTO "test" ("a", "b", "c") VALUES ('a1', 'b1', 'c1'), ('a2', 'b2', 'c2'), ('a3', 'b3', 'c3')
+}
+
+func ExampleInsertDataset_ClearVals() {
+ insertSQL, _, _ := goqu.Insert("test").
+ Cols("a", "b", "c").
+ Vals(
+ []interface{}{"a1", "b1", "c1"},
+ []interface{}{"a2", "b1", "c1"},
+ []interface{}{"a3", "b1", "c1"},
+ ).
+ ClearVals().
+ ToSQL()
+ fmt.Println(insertSQL)
+
+ insertSQL, _, _ = goqu.Insert("test").
+ Cols("a", "b", "c").
+ Vals([]interface{}{"a1", "b1", "c1"}).
+ Vals([]interface{}{"a2", "b2", "c2"}).
+ Vals([]interface{}{"a3", "b3", "c3"}).
+ ClearVals().
+ ToSQL()
+ fmt.Println(insertSQL)
+ // Output:
+ // INSERT INTO "test" DEFAULT VALUES
+ // INSERT INTO "test" DEFAULT VALUES
+}
diff --git a/insert_dataset_test.go b/insert_dataset_test.go
new file mode 100644
index 00000000..8ec5dc0d
--- /dev/null
+++ b/insert_dataset_test.go
@@ -0,0 +1,930 @@
+package goqu
+
+import (
+ "database/sql"
+ "testing"
+ "time"
+
+ "github.com/DATA-DOG/go-sqlmock"
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+ "github.com/doug-martin/goqu/v8/mocks"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+)
+
+type insertDatasetSuite struct {
+ suite.Suite
+}
+
+func (ids *insertDatasetSuite) SetupSuite() {
+ noReturn := DefaultDialectOptions()
+ noReturn.SupportsReturn = false
+ RegisterDialect("no-return", noReturn)
+}
+
+func (ids *insertDatasetSuite) TearDownSuite() {
+ DeregisterDialect("no-return")
+}
+
+func (ids *insertDatasetSuite) TestClone() {
+ t := ids.T()
+ ds := Insert("test")
+ assert.Equal(t, ds.Clone(), ds)
+}
+
+func (ids *insertDatasetSuite) TestExpression() {
+ t := ids.T()
+ ds := Insert("test")
+ assert.Equal(t, ds.Expression(), ds)
+}
+
+func (ids *insertDatasetSuite) TestDialect() {
+ t := ids.T()
+ ds := Insert("test")
+ assert.NotNil(t, ds.Dialect())
+}
+
+func (ids *insertDatasetSuite) TestWithDialect() {
+ t := ids.T()
+ ds := Insert("test")
+ dialect := GetDialect("default")
+ ds.WithDialect("default")
+ assert.Equal(t, ds.Dialect(), dialect)
+}
+
+func (ids *insertDatasetSuite) TestPrepared() {
+ t := ids.T()
+ ds := Insert("test")
+ preparedDs := ds.Prepared(true)
+ assert.True(t, preparedDs.IsPrepared())
+ assert.False(t, ds.IsPrepared())
+ // should apply the prepared to any datasets created from the root
+ assert.True(t, preparedDs.Returning(C("col")).IsPrepared())
+}
+
+func (ids *insertDatasetSuite) TestGetClauses() {
+ t := ids.T()
+ ds := Insert("test")
+ ce := exp.NewInsertClauses().SetInto(I("test"))
+ assert.Equal(t, ce, ds.GetClauses())
+}
+
+func (ids *insertDatasetSuite) TestWith() {
+ t := ids.T()
+ from := Insert("cte")
+ ds := Insert("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from))
+ assert.Equal(t, ec, ds.With("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (ids *insertDatasetSuite) TestWithRecursive() {
+ t := ids.T()
+ from := Insert("cte")
+ ds := Insert("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from))
+ assert.Equal(t, ec, ds.WithRecursive("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithNullTimeField() {
+ t := ids.T()
+ type item struct {
+ CreatedAt *time.Time `db:"created_at"`
+ }
+ ds := Insert("items").Rows(item{CreatedAt: nil})
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("created_at") VALUES (NULL)`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("created_at") VALUES (NULL)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithInvalidValue() {
+ t := ids.T()
+ ds := Insert("test").Rows(true)
+ _, _, err := ds.ToSQL()
+ assert.EqualError(t, err, "goqu: unsupported insert must be map, goqu.Record, or struct type got: bool")
+
+ _, _, err = ds.Prepared(true).ToSQL()
+ assert.EqualError(t, err, "goqu: unsupported insert must be map, goqu.Record, or struct type got: bool")
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithStructs() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ Created time.Time `db:"created"`
+ }
+ ds := Insert("items")
+ created, _ := time.Parse("2006-01-02", "2015-01-01")
+ ds1 := ds.Rows(item{Name: "Test", Address: "111 Test Addr", Created: created})
+
+ insertSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t,
+ `INSERT INTO "items" ("address", "created", "name") VALUES ('111 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test')`,
+ insertSQL,
+ ) // #nosec
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", created, "Test"})
+ assert.Equal(t, `INSERT INTO "items" ("address", "created", "name") VALUES (?, ?, ?)`, insertSQL)
+
+ ds2 := ds1.Rows(
+ item{Address: "111 Test Addr", Name: "Test1", Created: created},
+ item{Address: "211 Test Addr", Name: "Test2", Created: created},
+ item{Address: "311 Test Addr", Name: "Test3", Created: created},
+ item{Address: "411 Test Addr", Name: "Test4", Created: created},
+ )
+
+ insertSQL, args, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t,
+ `INSERT INTO "items" ("address", "created", "name") VALUES `+
+ `('111 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test1'), `+
+ `('211 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test2'), `+
+ `('311 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test3'), `+
+ `('411 Test Addr', '`+created.Format(time.RFC3339Nano)+`', 'Test4')`,
+ insertSQL,
+ )
+
+ insertSQL, args, err = ds2.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", created, "Test1",
+ "211 Test Addr", created, "Test2",
+ "311 Test Addr", created, "Test3",
+ "411 Test Addr", created, "Test4",
+ })
+ assert.Equal(t,
+ `INSERT INTO "items" ("address", "created", "name") VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?), (?, ?, ?)`,
+ insertSQL,
+ )
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithEmbeddedStruct() {
+ t := ids.T()
+ type Phone struct {
+ Primary string `db:"primary_phone"`
+ Home string `db:"home_phone"`
+ }
+ type item struct {
+ Phone
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ bd := Insert("items")
+ ds := bd.Rows(item{
+ Name: "Test",
+ Address: "111 Test Addr",
+ Phone: Phone{
+ Home: "123123",
+ Primary: "456456",
+ },
+ })
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
+ `('111 Test Addr', '123123', 'Test', '456456')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "123123", "Test", "456456"})
+ assert.Equal(
+ t,
+ `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES (?, ?, ?, ?)`,
+ insertSQL,
+ )
+
+ ds = bd.Rows(
+ item{Address: "111 Test Addr", Name: "Test1", Phone: Phone{Home: "123123", Primary: "456456"}},
+ item{Address: "211 Test Addr", Name: "Test2", Phone: Phone{Home: "123123", Primary: "456456"}},
+ item{Address: "311 Test Addr", Name: "Test3", Phone: Phone{Home: "123123", Primary: "456456"}},
+ item{Address: "411 Test Addr", Name: "Test4", Phone: Phone{Home: "123123", Primary: "456456"}},
+ )
+ insertSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
+ `('111 Test Addr', '123123', 'Test1', '456456'), `+
+ `('211 Test Addr', '123123', 'Test2', '456456'), `+
+ `('311 Test Addr', '123123', 'Test3', '456456'), `+
+ `('411 Test Addr', '123123', 'Test4', '456456')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", "123123", "Test1", "456456",
+ "211 Test Addr", "123123", "Test2", "456456",
+ "311 Test Addr", "123123", "Test3", "456456",
+ "411 Test Addr", "123123", "Test4", "456456",
+ })
+ assert.Equal(t, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
+ `(?, ?, ?, ?), `+
+ `(?, ?, ?, ?), `+
+ `(?, ?, ?, ?), `+
+ `(?, ?, ?, ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithEmbeddedStructPtr() {
+ t := ids.T()
+ type Phone struct {
+ Primary string `db:"primary_phone"`
+ Home string `db:"home_phone"`
+ }
+ type item struct {
+ *Phone
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ bd := Insert("items")
+ ds := bd.Rows(item{
+ Name: "Test",
+ Address: "111 Test Addr",
+ Phone: &Phone{
+ Home: "123123",
+ Primary: "456456",
+ },
+ })
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
+ `('111 Test Addr', '123123', 'Test', '456456')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "123123", "Test", "456456"})
+ assert.Equal(
+ t,
+ insertSQL,
+ `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES (?, ?, ?, ?)`,
+ )
+
+ ds = bd.Rows(
+ item{Address: "111 Test Addr", Name: "Test1", Phone: &Phone{Home: "123123", Primary: "456456"}},
+ item{Address: "211 Test Addr", Name: "Test2", Phone: &Phone{Home: "123123", Primary: "456456"}},
+ item{Address: "311 Test Addr", Name: "Test3", Phone: &Phone{Home: "123123", Primary: "456456"}},
+ item{Address: "411 Test Addr", Name: "Test4", Phone: &Phone{Home: "123123", Primary: "456456"}},
+ )
+ insertSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
+ `('111 Test Addr', '123123', 'Test1', '456456'), `+
+ `('211 Test Addr', '123123', 'Test2', '456456'), `+
+ `('311 Test Addr', '123123', 'Test3', '456456'), `+
+ `('411 Test Addr', '123123', 'Test4', '456456')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", "123123", "Test1", "456456",
+ "211 Test Addr", "123123", "Test2", "456456",
+ "311 Test Addr", "123123", "Test3", "456456",
+ "411 Test Addr", "123123", "Test4", "456456",
+ })
+ assert.Equal(t, `INSERT INTO "items" ("address", "home_phone", "name", "primary_phone") VALUES `+
+ `(?, ?, ?, ?), `+
+ `(?, ?, ?, ?), `+
+ `(?, ?, ?, ?), `+
+ `(?, ?, ?, ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithValuer() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ Valuer sql.NullInt64 `db:"valuer"`
+ }
+
+ bd := Insert("items")
+ ds := bd.Rows(item{Name: "Test", Address: "111 Test Addr", Valuer: sql.NullInt64{Int64: 10, Valid: true}})
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES ('111 Test Addr', 'Test', 10)`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test", int64(10)})
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES (?, ?, ?)`, insertSQL)
+
+ ds = bd.Rows(
+ item{Address: "111 Test Addr", Name: "Test1", Valuer: sql.NullInt64{Int64: 10, Valid: true}},
+ item{Address: "211 Test Addr", Name: "Test2", Valuer: sql.NullInt64{Int64: 20, Valid: true}},
+ item{Address: "311 Test Addr", Name: "Test3", Valuer: sql.NullInt64{Int64: 30, Valid: true}},
+ item{Address: "411 Test Addr", Name: "Test4", Valuer: sql.NullInt64{Int64: 40, Valid: true}},
+ )
+ insertSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
+ `('111 Test Addr', 'Test1', 10), `+
+ `('211 Test Addr', 'Test2', 20), `+
+ `('311 Test Addr', 'Test3', 30), `+
+ `('411 Test Addr', 'Test4', 40)`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", "Test1", int64(10),
+ "211 Test Addr", "Test2", int64(20),
+ "311 Test Addr", "Test3", int64(30),
+ "411 Test Addr", "Test4", int64(40),
+ })
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
+ `(?, ?, ?), `+
+ `(?, ?, ?), `+
+ `(?, ?, ?), `+
+ `(?, ?, ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithValuerNull() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ Valuer sql.NullInt64 `db:"valuer"`
+ }
+
+ bd := Insert("items")
+ ds := bd.Rows(item{Name: "Test", Address: "111 Test Addr"})
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES ('111 Test Addr', 'Test', NULL)`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES (?, ?, NULL)`, insertSQL)
+
+ ds = bd.Rows(
+ item{Address: "111 Test Addr", Name: "Test1"},
+ item{Address: "211 Test Addr", Name: "Test2"},
+ item{Address: "311 Test Addr", Name: "Test3"},
+ item{Address: "411 Test Addr", Name: "Test4"},
+ )
+ insertSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
+ `('111 Test Addr', 'Test1', NULL), `+
+ `('211 Test Addr', 'Test2', NULL), `+
+ `('311 Test Addr', 'Test3', NULL), `+
+ `('411 Test Addr', 'Test4', NULL)`,
+ insertSQL,
+ )
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", "Test1",
+ "211 Test Addr", "Test2",
+ "311 Test Addr", "Test3",
+ "411 Test Addr", "Test4",
+ })
+ assert.Equal(t, `INSERT INTO "items" ("address", "name", "valuer") VALUES `+
+ `(?, ?, NULL), `+
+ `(?, ?, NULL), `+
+ `(?, ?, NULL), `+
+ `(?, ?, NULL)`,
+ insertSQL,
+ )
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithMaps() {
+ t := ids.T()
+ ds := Insert("items")
+
+ ds1 := ds.Rows(map[string]interface{}{"name": "Test", "address": "111 Test Addr"})
+ insertSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`, insertSQL)
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`, insertSQL)
+
+ ds1 = ds.Rows(
+ map[string]interface{}{"address": "111 Test Addr", "name": "Test1"},
+ map[string]interface{}{"address": "211 Test Addr", "name": "Test2"},
+ map[string]interface{}{"address": "311 Test Addr", "name": "Test3"},
+ map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
+ )
+ insertSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
+ `('111 Test Addr', 'Test1'), `+
+ `('211 Test Addr', 'Test2'), `+
+ `('311 Test Addr', 'Test3'), `+
+ `('411 Test Addr', 'Test4')`,
+ insertSQL,
+ )
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", "Test1",
+ "211 Test Addr", "Test2",
+ "311 Test Addr", "Test3",
+ "411 Test Addr", "Test4",
+ })
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?), (?, ?), (?, ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithSQLBuilder() {
+ t := ids.T()
+ ds := Insert("items")
+
+ ds1 := ds.Rows(From("other_items").Where(C("b").Gt(10)))
+
+ insertSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > 10)`, insertSQL)
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(10)})
+ assert.Equal(t, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithMapsWithDifferentLengths() {
+ t := ids.T()
+ ds1 := Insert("items").Rows(
+ map[string]interface{}{"address": "111 Test Addr", "name": "Test1"},
+ map[string]interface{}{"address": "211 Test Addr"},
+ map[string]interface{}{"address": "311 Test Addr", "name": "Test3"},
+ map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
+ )
+ _, _, err := ds1.ToSQL()
+ assert.EqualError(t, err, "goqu: rows with different value length expected 2 got 1")
+ _, _, err = ds1.Prepared(true).ToSQL()
+ assert.EqualError(t, err, "goqu: rows with different value length expected 2 got 1")
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWitDifferentKeys() {
+ t := ids.T()
+ ds := Insert("items").Rows(
+ map[string]interface{}{"address": "111 Test Addr", "name": "test"},
+ map[string]interface{}{"phoneNumber": 10, "address": "111 Test Addr"},
+ )
+ _, _, err := ds.ToSQL()
+ assert.EqualError(
+ t,
+ err,
+ `goqu: rows with different keys expected ["address","name"] got ["address","phoneNumber"]`,
+ )
+
+ _, _, err = ds.Prepared(true).ToSQL()
+ assert.EqualError(
+ t,
+ err,
+ `goqu: rows with different keys expected ["address","name"] got ["address","phoneNumber"]`,
+ )
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLDifferentTypes() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ type item2 struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ bd := Insert("items")
+ ds := bd.Rows(
+ item{Address: "111 Test Addr", Name: "Test1"},
+ item2{Address: "211 Test Addr", Name: "Test2"},
+ item{Address: "311 Test Addr", Name: "Test3"},
+ item2{Address: "411 Test Addr", Name: "Test4"},
+ )
+ _, _, err := ds.ToSQL()
+ assert.EqualError(t, err, "goqu: rows must be all the same type expected goqu.item got goqu.item2")
+ _, _, err = ds.Prepared(true).ToSQL()
+ assert.EqualError(t, err, "goqu: rows must be all the same type expected goqu.item got goqu.item2")
+
+ ds = bd.Rows(
+ item{Address: "111 Test Addr", Name: "Test1"},
+ map[string]interface{}{"address": "211 Test Addr", "name": "Test2"},
+ item{Address: "311 Test Addr", Name: "Test3"},
+ map[string]interface{}{"address": "411 Test Addr", "name": "Test4"},
+ )
+ _, _, err = ds.ToSQL()
+ assert.EqualError(
+ t,
+ err,
+ "goqu: rows must be all the same type expected goqu.item got map[string]interface {}",
+ )
+
+ _, _, err = ds.Prepared(true).ToSQL()
+ assert.EqualError(
+ t,
+ err,
+ "goqu: rows must be all the same type expected goqu.item got map[string]interface {}",
+ )
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithGoquSkipInsertTagSQL() {
+ t := ids.T()
+ type item struct {
+ ID uint32 `db:"id" goqu:"skipinsert"`
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ ds := Insert("items")
+
+ ds1 := ds.Rows(item{Name: "Test", Address: "111 Test Addr"})
+
+ insertSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`, insertSQL)
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`, insertSQL)
+
+ ds1 = ds.Rows(
+ item{Name: "Test1", Address: "111 Test Addr"},
+ item{Name: "Test2", Address: "211 Test Addr"},
+ item{Name: "Test3", Address: "311 Test Addr"},
+ item{Name: "Test4", Address: "411 Test Addr"},
+ )
+
+ insertSQL, args, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
+ `('111 Test Addr', 'Test1'), `+
+ `('211 Test Addr', 'Test2'), `+
+ `('311 Test Addr', 'Test3'), `+
+ `('411 Test Addr', 'Test4')`,
+ insertSQL,
+ )
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{
+ "111 Test Addr", "Test1",
+ "211 Test Addr", "Test2",
+ "311 Test Addr", "Test3",
+ "411 Test Addr", "Test4",
+ })
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?), (?, ?), (?, ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestRows_ToSQLWithDefaultValues() {
+ t := ids.T()
+ ds := Insert("items")
+ ds1 := ds.Rows()
+
+ insertSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" DEFAULT VALUES`, insertSQL)
+
+ insertSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" DEFAULT VALUES`, insertSQL)
+
+ ds1 = ds.Rows(map[string]interface{}{"name": Default(), "address": Default()})
+ insertSQL, args, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (DEFAULT, DEFAULT)`, insertSQL)
+
+ insertSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (DEFAULT, DEFAULT)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestToSQL() {
+ t := ids.T()
+ md := new(mocks.SQLDialect)
+ ds := Insert("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToInsertSQL", sqlB, c).Return(nil).Once()
+ insertSQL, args, err := ds.ToSQL()
+ assert.Empty(t, insertSQL)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (ids *insertDatasetSuite) TestToSQL_WithNoInto() {
+ t := ids.T()
+ ds1 := newInsertDataset("test", nil).Rows(map[string]interface{}{
+ "address": "111 Test Addr", "name": "Test1",
+ })
+ _, _, err := ds1.ToSQL()
+ assert.EqualError(t, err, "goqu: no source found when generating insert sql")
+ _, _, err = ds1.Prepared(true).ToSQL()
+ assert.EqualError(t, err, "goqu: no source found when generating insert sql")
+}
+
+func (ids *insertDatasetSuite) TestToSQL_ReturnedError() {
+ t := ids.T()
+ md := new(mocks.SQLDialect)
+ ds := Insert("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ ee := errors.New("expected error")
+ md.On("ToInsertSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(ee)
+ }).Once()
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.Empty(t, insertSQL)
+ assert.Empty(t, args)
+ assert.Equal(t, ee, err)
+ md.AssertExpectations(t)
+}
+
+func (ids *insertDatasetSuite) TestFromQuery_ToSQL() {
+ t := ids.T()
+ bd := Insert("items")
+
+ ds := bd.FromQuery(From("other_items").Where(C("b").Gt(10)))
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > 10)`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(10)})
+ assert.Equal(t, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestFromQuery_ToSQLWithCols() {
+ t := ids.T()
+ bd := Insert("items")
+
+ ds := bd.Cols("a", "b").FromQuery(From("other_items").Select("c", "d").Where(C("b").Gt(10)))
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("a", "b") SELECT "c", "d" FROM "other_items" WHERE ("b" > 10)`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(10)})
+ assert.Equal(t, `INSERT INTO "items" ("a", "b") SELECT "c", "d" FROM "other_items" WHERE ("b" > ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestOnConflict__ToSQLNilConflictExpression() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ ds := Insert("items").Rows(item{Name: "Test", Address: "111 Test Addr"}).OnConflict(nil)
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test"}, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestOnConflict__ToSQLDoUpdate() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ i := item{Name: "Test", Address: "111 Test Addr"}
+ ds := Insert("items").Rows(i).OnConflict(
+ DoUpdate("name", Record{"address": L("excluded.address")}),
+ )
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
+ `('111 Test Addr', 'Test') `+
+ `ON CONFLICT (name) `+
+ `DO UPDATE `+
+ `SET "address"=excluded.address`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test"}, args)
+ assert.Equal(
+ t,
+ `INSERT INTO "items" ("address", "name") VALUES (?, ?) ON CONFLICT (name) DO UPDATE SET "address"=excluded.address`,
+ insertSQL,
+ )
+}
+
+func (ids *insertDatasetSuite) TestOnConflict__ToSQLDoUpdateWhere() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ i := item{Name: "Test", Address: "111 Test Addr"}
+ ds := Insert("items").Rows(i).OnConflict(
+ DoUpdate("name", Record{"address": L("excluded.address")}).
+ Where(C("name").Eq("Test")),
+ )
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
+ `('111 Test Addr', 'Test') `+
+ `ON CONFLICT (name) `+
+ `DO UPDATE `+
+ `SET "address"=excluded.address WHERE ("name" = 'Test')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test", "Test"}, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
+ `(?, ?) `+
+ `ON CONFLICT (name) `+
+ `DO UPDATE `+
+ `SET "address"=excluded.address WHERE ("name" = ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestOnConflict__ToSQLWithDatasetDoUpdateWhere() {
+ t := ids.T()
+ fromDs := From("ds2")
+ ds := Insert("items").
+ FromQuery(fromDs).
+ OnConflict(
+ DoUpdate("name", Record{"address": L("excluded.address")}).Where(C("name").Eq("Test")),
+ )
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" `+
+ `SELECT * FROM "ds2" `+
+ `ON CONFLICT (name) `+
+ `DO UPDATE `+
+ `SET "address"=excluded.address WHERE ("name" = 'Test')`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"Test"}, args)
+ assert.Equal(t, `INSERT INTO "items" `+
+ `SELECT * FROM "ds2" `+
+ `ON CONFLICT (name) `+
+ `DO UPDATE `+
+ `SET "address"=excluded.address WHERE ("name" = ?)`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestOnConflict_ToSQLDoNothing() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ ds := Insert("items").Rows(item{Name: "Test", Address: "111 Test Addr"}).OnConflict(DoNothing())
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES `+
+ `('111 Test Addr', 'Test') `+
+ `ON CONFLICT DO NOTHING`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test"}, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?) ON CONFLICT DO NOTHING`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestReturning() {
+ t := ids.T()
+ ds := Insert("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetReturning(exp.NewColumnListExpression(C("a")))
+ assert.Equal(t, ec, ds.Returning("a").GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (ids *insertDatasetSuite) TestReturning_ToSQL() {
+ t := ids.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ bd := Insert("items").Returning("id")
+
+ ds := bd.FromQuery(From("other_items").Where(C("b").Gt(10)))
+
+ insertSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > 10) RETURNING "id"`, insertSQL)
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(10)})
+ assert.Equal(t, `INSERT INTO "items" SELECT * FROM "other_items" WHERE ("b" > ?) RETURNING "id"`, insertSQL)
+
+ ds = bd.Rows(map[string]interface{}{"name": "Test", "address": "111 Test Addr"})
+
+ insertSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(
+ t,
+ insertSQL,
+ `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test') RETURNING "id"`,
+ )
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?) RETURNING "id"`, insertSQL)
+
+ ds = bd.Rows(item{Name: "Test", Address: "111 Test Addr"})
+
+ insertSQL, _, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(
+ t,
+ `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test') RETURNING "id"`,
+ insertSQL,
+ )
+
+ insertSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?) RETURNING "id"`, insertSQL)
+}
+
+func (ids *insertDatasetSuite) TestReturning_ToSQLReturnNotSupported() {
+ t := ids.T()
+ ds1 := New("no-return", nil).Insert("items")
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ _, _, err := ds1.Returning("id").Rows(item{Name: "Test", Address: "111 Test Addr"}).ToSQL()
+ assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
+
+ _, _, err = ds1.Returning("id").Rows(From("test2")).ToSQL()
+ assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
+}
+
+func (ids *insertDatasetSuite) TestExecutor() {
+ t := ids.T()
+ mDb, _, err := sqlmock.New()
+ assert.NoError(t, err)
+
+ ds := newInsertDataset("mock", exec.NewQueryFactory(mDb)).
+ Into("items").
+ Rows(Record{"address": "111 Test Addr", "name": "Test1"})
+
+ isql, args, err := ds.Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1')`, isql)
+
+ isql, args, err = ds.Prepared(true).Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test1"}, args)
+ assert.Equal(t, `INSERT INTO "items" ("address", "name") VALUES (?, ?)`, isql)
+}
+
+func TestInsertDataset(t *testing.T) {
+ suite.Run(t, new(insertDatasetSuite))
+}
diff --git a/internal/util/reflect.go b/internal/util/reflect.go
index 9996af70..d1c5b6b9 100644
--- a/internal/util/reflect.go
+++ b/internal/util/reflect.go
@@ -6,8 +6,8 @@ import (
"strings"
"sync"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/tag"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/tag"
)
type (
diff --git a/issues_test.go b/issues_test.go
index d2ffda11..f89ee81a 100644
--- a/issues_test.go
+++ b/issues_test.go
@@ -3,7 +3,7 @@ package goqu_test
import (
"testing"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
diff --git a/mocks/SQLDialect.go b/mocks/SQLDialect.go
index 31e227c6..e6745211 100644
--- a/mocks/SQLDialect.go
+++ b/mocks/SQLDialect.go
@@ -2,37 +2,51 @@
package mocks
-import exp "github.com/doug-martin/goqu/v7/exp"
+import exp "github.com/doug-martin/goqu/v8/exp"
import mock "github.com/stretchr/testify/mock"
-import sb "github.com/doug-martin/goqu/v7/internal/sb"
+import sb "github.com/doug-martin/goqu/v8/internal/sb"
// SQLDialect is an autogenerated mock type for the SQLDialect type
type SQLDialect struct {
mock.Mock
}
+// Dialect provides a mock function with given fields:
+func (_m *SQLDialect) Dialect() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
// ToDeleteSQL provides a mock function with given fields: b, clauses
-func (_m *SQLDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.Clauses) {
+func (_m *SQLDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.DeleteClauses) {
_m.Called(b, clauses)
}
-// ToInsertSQL provides a mock function with given fields: b, clauses, ie
-func (_m *SQLDialect) ToInsertSQL(b sb.SQLBuilder, clauses exp.Clauses, ie exp.InsertExpression) {
- _m.Called(b, clauses, ie)
+// ToInsertSQL provides a mock function with given fields: b, clauses
+func (_m *SQLDialect) ToInsertSQL(b sb.SQLBuilder, clauses exp.InsertClauses) {
+ _m.Called(b, clauses)
}
// ToSelectSQL provides a mock function with given fields: b, clauses
-func (_m *SQLDialect) ToSelectSQL(b sb.SQLBuilder, clauses exp.Clauses) {
+func (_m *SQLDialect) ToSelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
_m.Called(b, clauses)
}
-// ToTruncateSQL provides a mock function with given fields: b, clauses, options
-func (_m *SQLDialect) ToTruncateSQL(b sb.SQLBuilder, clauses exp.Clauses, options exp.TruncateOptions) {
- _m.Called(b, clauses, options)
+// ToTruncateSQL provides a mock function with given fields: b, clauses
+func (_m *SQLDialect) ToTruncateSQL(b sb.SQLBuilder, clauses exp.TruncateClauses) {
+ _m.Called(b, clauses)
}
-// ToUpdateSQL provides a mock function with given fields: b, clauses, update
-func (_m *SQLDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.Clauses, update interface{}) {
- _m.Called(b, clauses, update)
+// ToUpdateSQL provides a mock function with given fields: b, clauses
+func (_m *SQLDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.UpdateClauses) {
+ _m.Called(b, clauses)
}
diff --git a/select_dataset.go b/select_dataset.go
new file mode 100644
index 00000000..d7f0f735
--- /dev/null
+++ b/select_dataset.go
@@ -0,0 +1,633 @@
+package goqu
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+)
+
+// Dataset for creating and/or executing SELECT SQL statements.
+type SelectDataset struct {
+ dialect SQLDialect
+ clauses exp.SelectClauses
+ isPrepared bool
+ queryFactory exec.QueryFactory
+}
+
+var (
+ errQueryFactoryNotFoundError = errors.New(
+ "unable to execute query did you use goqu.Database#From to create the dataset",
+ )
+)
+
+// used internally by database to create a database with a specific adapter
+func newDataset(d string, queryFactory exec.QueryFactory) *SelectDataset {
+ return &SelectDataset{
+ clauses: exp.NewSelectClauses(),
+ dialect: GetDialect(d),
+ queryFactory: queryFactory,
+ }
+}
+
+func From(table ...interface{}) *SelectDataset {
+ return newDataset("default", nil).From(table...)
+}
+
+func Select(cols ...interface{}) *SelectDataset {
+ return newDataset("default", nil).Select(cols...)
+}
+
+// Sets the adapter used to serialize values and create the SQL statement
+func (sd *SelectDataset) WithDialect(dl string) *SelectDataset {
+ ds := sd.copy(sd.GetClauses())
+ ds.dialect = GetDialect(dl)
+ return ds
+}
+
+// Set the parameter interpolation behavior. See examples
+//
+// 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
+ return ret
+}
+
+func (sd *SelectDataset) IsPrepared() bool {
+ return sd.isPrepared
+}
+
+// Returns the current adapter on the dataset
+func (sd *SelectDataset) Dialect() SQLDialect {
+ return sd.dialect
+}
+
+// Returns the current adapter on the dataset
+func (sd *SelectDataset) SetDialect(dialect SQLDialect) *SelectDataset {
+ cd := sd.copy(sd.GetClauses())
+ cd.dialect = dialect
+ return cd
+}
+
+func (sd *SelectDataset) Expression() exp.Expression {
+ return sd
+}
+
+// Clones the dataset
+func (sd *SelectDataset) Clone() exp.Expression {
+ return sd.copy(sd.clauses)
+}
+
+// Returns the current clauses on the dataset.
+func (sd *SelectDataset) GetClauses() exp.SelectClauses {
+ return sd.clauses
+}
+
+// used interally to copy the dataset
+func (sd *SelectDataset) copy(clauses exp.SelectClauses) *SelectDataset {
+ return &SelectDataset{
+ dialect: sd.dialect,
+ clauses: clauses,
+ isPrepared: sd.isPrepared,
+ queryFactory: sd.queryFactory,
+ }
+}
+
+// Creates a new UpdateDataset using the FROM of this dataset. This method will also copy over the `WITH`, `WHERE`,
+// `ORDER , and `LIMIT`
+func (sd *SelectDataset) Update() *UpdateDataset {
+ u := newUpdateDataset(sd.dialect.Dialect(), sd.queryFactory).
+ Prepared(sd.isPrepared)
+ if sd.clauses.HasSources() {
+ u = u.Table(sd.GetClauses().From().Columns()[0])
+ }
+ c := u.clauses
+ for _, ce := range sd.clauses.CommonTables() {
+ c = c.CommonTablesAppend(ce)
+ }
+ if sd.clauses.Where() != nil {
+ c = c.WhereAppend(sd.clauses.Where())
+ }
+ if c.HasLimit() {
+ c = c.SetLimit(sd.clauses.Limit())
+ }
+ if sd.clauses.HasOrder() {
+ for _, oe := range sd.clauses.Order().Columns() {
+ c = c.OrderAppend(oe.(exp.OrderedExpression))
+ }
+ }
+ u.clauses = c
+ return u
+}
+
+// Creates a new InsertDataset using the FROM of this dataset. This method will also copy over the `WITH` clause to the
+// insert.
+func (sd *SelectDataset) Insert() *InsertDataset {
+ i := newInsertDataset(sd.dialect.Dialect(), sd.queryFactory).
+ Prepared(sd.isPrepared)
+ if sd.clauses.HasSources() {
+ i = i.Into(sd.GetClauses().From().Columns()[0])
+ }
+ c := i.clauses
+ for _, ce := range sd.clauses.CommonTables() {
+ c = c.CommonTablesAppend(ce)
+ }
+ i.clauses = c
+ return i
+}
+
+// Creates a new DeleteDataset using the FROM of this dataset. This method will also copy over the `WITH`, `WHERE`,
+// `ORDER , and `LIMIT`
+func (sd *SelectDataset) Delete() *DeleteDataset {
+ d := newDeleteDataset(sd.dialect.Dialect(), sd.queryFactory).
+ Prepared(sd.isPrepared)
+ if sd.clauses.HasSources() {
+ d = d.From(sd.clauses.From().Columns()[0])
+ }
+ c := d.clauses
+ for _, ce := range sd.clauses.CommonTables() {
+ c = c.CommonTablesAppend(ce)
+ }
+ if sd.clauses.Where() != nil {
+ c = c.WhereAppend(sd.clauses.Where())
+ }
+ if sd.clauses.HasLimit() {
+ c = c.SetLimit(sd.clauses.Limit())
+ }
+ if sd.clauses.HasOrder() {
+ for _, oe := range sd.clauses.Order().Columns() {
+ c = c.OrderAppend(oe.(exp.OrderedExpression))
+ }
+ }
+ d.clauses = c
+ return d
+}
+
+// Creates a new TruncateDataset using the FROM of this dataset.
+func (sd *SelectDataset) Truncate() *TruncateDataset {
+ td := newTruncateDataset(sd.dialect.Dialect(), sd.queryFactory)
+ if sd.clauses.HasSources() {
+ td = td.Table(sd.clauses.From())
+ }
+ return td
+}
+
+// Creates a WITH clause for a common table expression (CTE).
+//
+// The name will be available to SELECT from in the associated query; and can optionally
+// contain a list of column names "name(col1, col2, col3)".
+//
+// The name will refer to the results of the specified subquery.
+func (sd *SelectDataset) With(name string, subquery exp.Expression) *SelectDataset {
+ return sd.copy(sd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
+}
+
+// Creates a WITH RECURSIVE clause for a common table expression (CTE)
+//
+// The name will be available to SELECT from in the associated query; and must
+// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
+//
+// The name will refer to the results of the specified subquery. The subquery for
+// a recursive query will always end with a UNION or UNION ALL with a clause that
+// refers to the CTE by name.
+func (sd *SelectDataset) WithRecursive(name string, subquery exp.Expression) *SelectDataset {
+ return sd.copy(sd.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
+}
+
+// Adds columns to the SELECT clause. See examples
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
+// column name.
+// LiteralExpression: (See Literal) Will use the literal SQL
+// SQLFunction: (See Func, MIN, MAX, COUNT....)
+// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
+// See examples
+func (sd *SelectDataset) Select(selects ...interface{}) *SelectDataset {
+ if len(selects) == 0 {
+ return sd.ClearSelect()
+ }
+ return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(selects...)))
+}
+
+// Adds columns to the SELECT DISTINCT clause. See examples
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
+// column name.
+// LiteralExpression: (See Literal) Will use the literal SQL
+// SQLFunction: (See Func, MIN, MAX, COUNT....)
+// Struct: If passing in an instance of a struct, we will parse the struct for the column names to select.
+// See examples
+func (sd *SelectDataset) SelectDistinct(selects ...interface{}) *SelectDataset {
+ return sd.copy(sd.clauses.SetSelectDistinct(exp.NewColumnListExpression(selects...)))
+}
+
+// Resets to SELECT *. If the SelectDistinct was used the returned Dataset will have the the dataset set to SELECT *.
+// See examples.
+func (sd *SelectDataset) ClearSelect() *SelectDataset {
+ return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(exp.Star())))
+}
+
+// Adds columns to the SELECT clause. See examples
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// Dataset: Will use the SQL generated from that Dataset. If the dataset is aliased it will use that alias as the
+// column name.
+// LiteralExpression: (See Literal) Will use the literal SQL
+// SQLFunction: (See Func, MIN, MAX, COUNT....)
+func (sd *SelectDataset) SelectAppend(selects ...interface{}) *SelectDataset {
+ return sd.copy(sd.clauses.SelectAppend(exp.NewColumnListExpression(selects...)))
+}
+
+// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// Dataset: Will be added as a sub select. If the Dataset is not aliased it will automatically be aliased
+// LiteralExpression: (See Literal) Will use the literal SQL
+func (sd *SelectDataset) From(from ...interface{}) *SelectDataset {
+ var sources []interface{}
+ numSources := 0
+ for _, source := range from {
+ if ds, ok := source.(*SelectDataset); ok && !ds.clauses.HasAlias() {
+ numSources++
+ sources = append(sources, ds.As(fmt.Sprintf("t%d", numSources)))
+ } else {
+ sources = append(sources, source)
+ }
+ }
+ return sd.copy(sd.clauses.SetFrom(exp.NewColumnListExpression(sources...)))
+}
+
+// Returns a new Dataset with the current one as an source. If the current Dataset is not aliased (See Dataset#As) then
+// it will automatically be aliased. See examples.
+func (sd *SelectDataset) FromSelf() *SelectDataset {
+ builder := SelectDataset{
+ dialect: sd.dialect,
+ clauses: exp.NewSelectClauses(),
+ }
+ return builder.From(sd)
+
+}
+
+// Alias to InnerJoin. See examples.
+func (sd *SelectDataset) Join(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.InnerJoin(table, condition)
+}
+
+// Adds an INNER JOIN clause. See examples.
+func (sd *SelectDataset) InnerJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.InnerJoinType, table, condition))
+}
+
+// Adds a FULL OUTER JOIN clause. See examples.
+func (sd *SelectDataset) FullOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.FullOuterJoinType, table, condition))
+}
+
+// Adds a RIGHT OUTER JOIN clause. See examples.
+func (sd *SelectDataset) RightOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.RightOuterJoinType, table, condition))
+}
+
+// Adds a LEFT OUTER JOIN clause. See examples.
+func (sd *SelectDataset) LeftOuterJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.LeftOuterJoinType, table, condition))
+}
+
+// Adds a FULL JOIN clause. See examples.
+func (sd *SelectDataset) FullJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.FullJoinType, table, condition))
+}
+
+// Adds a RIGHT JOIN clause. See examples.
+func (sd *SelectDataset) RightJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.RightJoinType, table, condition))
+}
+
+// Adds a LEFT JOIN clause. See examples.
+func (sd *SelectDataset) LeftJoin(table exp.Expression, condition exp.JoinCondition) *SelectDataset {
+ return sd.joinTable(exp.NewConditionedJoinExpression(exp.LeftJoinType, table, condition))
+}
+
+// Adds a NATURAL JOIN clause. See examples.
+func (sd *SelectDataset) NaturalJoin(table exp.Expression) *SelectDataset {
+ return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, table))
+}
+
+// Adds a NATURAL LEFT JOIN clause. See examples.
+func (sd *SelectDataset) NaturalLeftJoin(table exp.Expression) *SelectDataset {
+ return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalLeftJoinType, table))
+}
+
+// Adds a NATURAL RIGHT JOIN clause. See examples.
+func (sd *SelectDataset) NaturalRightJoin(table exp.Expression) *SelectDataset {
+ return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalRightJoinType, table))
+}
+
+// Adds a NATURAL FULL JOIN clause. See examples.
+func (sd *SelectDataset) NaturalFullJoin(table exp.Expression) *SelectDataset {
+ return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.NaturalFullJoinType, table))
+}
+
+// Adds a CROSS JOIN clause. See examples.
+func (sd *SelectDataset) CrossJoin(table exp.Expression) *SelectDataset {
+ return sd.joinTable(exp.NewUnConditionedJoinExpression(exp.CrossJoinType, table))
+}
+
+// Joins this Datasets table with another
+func (sd *SelectDataset) joinTable(join exp.JoinExpression) *SelectDataset {
+ return sd.copy(sd.clauses.JoinsAppend(join))
+}
+
+// Adds a WHERE clause. See examples.
+func (sd *SelectDataset) Where(expressions ...exp.Expression) *SelectDataset {
+ return sd.copy(sd.clauses.WhereAppend(expressions...))
+}
+
+// Removes the WHERE clause. See examples.
+func (sd *SelectDataset) ClearWhere() *SelectDataset {
+ return sd.copy(sd.clauses.ClearWhere())
+}
+
+// Adds a FOR UPDATE clause. See examples.
+func (sd *SelectDataset) ForUpdate(waitOption exp.WaitOption) *SelectDataset {
+ return sd.withLock(exp.ForUpdate, waitOption)
+}
+
+// Adds a FOR NO KEY UPDATE clause. See examples.
+func (sd *SelectDataset) ForNoKeyUpdate(waitOption exp.WaitOption) *SelectDataset {
+ return sd.withLock(exp.ForNoKeyUpdate, waitOption)
+}
+
+// Adds a FOR KEY SHARE clause. See examples.
+func (sd *SelectDataset) ForKeyShare(waitOption exp.WaitOption) *SelectDataset {
+ return sd.withLock(exp.ForKeyShare, waitOption)
+}
+
+// Adds a FOR SHARE clause. See examples.
+func (sd *SelectDataset) ForShare(waitOption exp.WaitOption) *SelectDataset {
+ return sd.withLock(exp.ForShare, waitOption)
+}
+
+func (sd *SelectDataset) withLock(strength exp.LockStrength, option exp.WaitOption) *SelectDataset {
+ return sd.copy(sd.clauses.SetLock(exp.NewLock(strength, option)))
+}
+
+// Adds a GROUP BY clause. See examples.
+func (sd *SelectDataset) GroupBy(groupBy ...interface{}) *SelectDataset {
+ return sd.copy(sd.clauses.SetGroupBy(exp.NewColumnListExpression(groupBy...)))
+}
+
+// Adds a HAVING clause. See examples.
+func (sd *SelectDataset) Having(expressions ...exp.Expression) *SelectDataset {
+ return sd.copy(sd.clauses.HavingAppend(expressions...))
+}
+
+// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
+func (sd *SelectDataset) Order(order ...exp.OrderedExpression) *SelectDataset {
+ return sd.copy(sd.clauses.SetOrder(order...))
+}
+
+// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
+// calling Order. See examples.
+func (sd *SelectDataset) OrderAppend(order ...exp.OrderedExpression) *SelectDataset {
+ return sd.copy(sd.clauses.OrderAppend(order...))
+}
+
+// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
+// calling Order. See examples.
+func (sd *SelectDataset) OrderPrepend(order ...exp.OrderedExpression) *SelectDataset {
+ return sd.copy(sd.clauses.OrderPrepend(order...))
+}
+
+// Removes the ORDER BY clause. See examples.
+func (sd *SelectDataset) ClearOrder() *SelectDataset {
+ return sd.copy(sd.clauses.ClearOrder())
+}
+
+// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
+func (sd *SelectDataset) Limit(limit uint) *SelectDataset {
+ if limit > 0 {
+ return sd.copy(sd.clauses.SetLimit(limit))
+ }
+ return sd.copy(sd.clauses.ClearLimit())
+}
+
+// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
+func (sd *SelectDataset) LimitAll() *SelectDataset {
+ return sd.copy(sd.clauses.SetLimit(L("ALL")))
+}
+
+// Removes the LIMIT clause.
+func (sd *SelectDataset) ClearLimit() *SelectDataset {
+ return sd.copy(sd.clauses.ClearLimit())
+}
+
+// Adds an OFFSET clause. If the OFFSET is currently set it replaces it. See examples.
+func (sd *SelectDataset) Offset(offset uint) *SelectDataset {
+ return sd.copy(sd.clauses.SetOffset(offset))
+}
+
+// Removes the OFFSET clause from the Dataset
+func (sd *SelectDataset) ClearOffset() *SelectDataset {
+ return sd.copy(sd.clauses.ClearOffset())
+}
+
+// Creates an UNION statement with another dataset.
+// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
+// See examples.
+func (sd *SelectDataset) Union(other *SelectDataset) *SelectDataset {
+ return sd.withCompound(exp.UnionCompoundType, other.CompoundFromSelf())
+}
+
+// Creates an UNION ALL statement with another dataset.
+// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
+// See examples.
+func (sd *SelectDataset) UnionAll(other *SelectDataset) *SelectDataset {
+ return sd.withCompound(exp.UnionAllCompoundType, other.CompoundFromSelf())
+}
+
+// Creates an INTERSECT statement with another dataset.
+// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
+// See examples.
+func (sd *SelectDataset) Intersect(other *SelectDataset) *SelectDataset {
+ return sd.withCompound(exp.IntersectCompoundType, other.CompoundFromSelf())
+}
+
+// Creates an INTERSECT ALL statement with another dataset.
+// If this or the other dataset has a limit or offset it will use that dataset as a subselect in the FROM clause.
+// See examples.
+func (sd *SelectDataset) IntersectAll(other *SelectDataset) *SelectDataset {
+ return sd.withCompound(exp.IntersectAllCompoundType, other.CompoundFromSelf())
+}
+
+func (sd *SelectDataset) withCompound(ct exp.CompoundType, other exp.AppendableExpression) *SelectDataset {
+ ce := exp.NewCompoundExpression(ct, other)
+ ret := sd.CompoundFromSelf()
+ ret.clauses = ret.clauses.CompoundsAppend(ce)
+ return ret
+}
+
+// Used internally to determine if the dataset needs to use iteself as a source.
+// If the dataset has an order or limit it will select from itself
+func (sd *SelectDataset) CompoundFromSelf() *SelectDataset {
+ if sd.clauses.HasOrder() || sd.clauses.HasLimit() {
+ return sd.FromSelf()
+ }
+ return sd.copy(sd.clauses)
+}
+
+// Sets the alias for this dataset. This is typically used when using a Dataset as a subselect. See examples.
+func (sd *SelectDataset) As(alias string) *SelectDataset {
+ return sd.copy(sd.clauses.SetAlias(T(alias)))
+}
+
+// Generates a SELECT sql statement, if Prepared has been called with true then the parameters will not be interpolated.
+// See examples.
+//
+// Errors:
+// * There is an error generating the SQL
+func (sd *SelectDataset) ToSQL() (sql string, params []interface{}, err error) {
+ return sd.selectSQLBuilder().ToSQL()
+}
+
+// Generates the SELECT sql, and returns an Exec struct with the sql set to the SELECT statement
+// db.From("test").Select("col").Executor()
+//
+// See Dataset#ToUpdateSQL for arguments
+func (sd *SelectDataset) Executor() exec.QueryExecutor {
+ return sd.queryFactory.FromSQLBuilder(sd.selectSQLBuilder())
+}
+
+// Appends this Dataset's SELECT statement to the SQLBuilder
+// This is used internally for sub-selects by the dialect
+func (sd *SelectDataset) AppendSQL(b sb.SQLBuilder) {
+ sd.dialect.ToSelectSQL(b, sd.GetClauses())
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanStructs to scan the results into a slice of structs.
+//
+// ScanStructs will only select the columns that can be scanned in to the struct unless you have explicitly selected
+// certain columns. See examples.
+//
+// i: A pointer to a slice of structs
+func (sd *SelectDataset) ScanStructs(i interface{}) error {
+ return sd.ScanStructsContext(context.Background(), i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanStructsContext to scan the results into a slice of
+// structs.
+//
+// ScanStructsContext will only select the columns that can be scanned in to the struct unless you have explicitly
+// selected certain columns. See examples.
+//
+// i: A pointer to a slice of structs
+func (sd *SelectDataset) ScanStructsContext(ctx context.Context, i interface{}) error {
+ ds := sd
+ if sd.GetClauses().IsDefaultSelect() {
+ ds = sd.Select(i)
+ }
+ return ds.Executor().ScanStructsContext(ctx, i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanStruct to scan the result into a slice of structs
+//
+// ScanStruct will only select the columns that can be scanned in to the struct unless you have explicitly selected
+// certain columns. See examples.
+//
+// i: A pointer to a structs
+func (sd *SelectDataset) ScanStruct(i interface{}) (bool, error) {
+ return sd.ScanStructContext(context.Background(), i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanStructContext to scan the result into a slice of structs
+//
+// ScanStructContext will only select the columns that can be scanned in to the struct unless you have explicitly
+// selected certain columns. See examples.
+//
+// i: A pointer to a structs
+func (sd *SelectDataset) ScanStructContext(ctx context.Context, i interface{}) (bool, error) {
+ if sd.queryFactory == nil {
+ return false, errQueryFactoryNotFoundError
+ }
+ ds := sd
+ if sd.GetClauses().IsDefaultSelect() {
+ ds = sd.Select(i)
+ }
+ return ds.Limit(1).Executor().ScanStructContext(ctx, i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanVals to scan the results into a slice of primitive values
+//
+// i: A pointer to a slice of primitive values
+func (sd *SelectDataset) ScanVals(i interface{}) error {
+ return sd.ScanValsContext(context.Background(), i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanValsContext to scan the results into a slice of primitive
+// values
+//
+// i: A pointer to a slice of primitive values
+func (sd *SelectDataset) ScanValsContext(ctx context.Context, i interface{}) error {
+ if sd.queryFactory == nil {
+ return errQueryFactoryNotFoundError
+ }
+ return sd.Executor().ScanValsContext(ctx, i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanVal to scan the result into a primitive value
+//
+// i: A pointer to a primitive value
+func (sd *SelectDataset) ScanVal(i interface{}) (bool, error) {
+ return sd.ScanValContext(context.Background(), i)
+}
+
+// Generates the SELECT sql for this dataset and uses Exec#ScanValContext to scan the result into a primitive value
+//
+// i: A pointer to a primitive value
+func (sd *SelectDataset) ScanValContext(ctx context.Context, i interface{}) (bool, error) {
+ return sd.Limit(1).Executor().ScanValContext(ctx, i)
+}
+
+// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanVal to scan the result into an int64.
+func (sd *SelectDataset) Count() (int64, error) {
+ return sd.CountContext(context.Background())
+}
+
+// Generates the SELECT COUNT(*) sql for this dataset and uses Exec#ScanValContext to scan the result into an int64.
+func (sd *SelectDataset) CountContext(ctx context.Context) (int64, error) {
+ var count int64
+ _, err := sd.Select(COUNT(Star()).As("count")).ScanValContext(ctx, &count)
+ return count, err
+}
+
+// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanVals to scan the result into a slice
+// of primitive values.
+//
+// i: A slice of primitive values
+//
+// col: The column to select when generative the SQL
+func (sd *SelectDataset) Pluck(i interface{}, col string) error {
+ return sd.PluckContext(context.Background(), i, col)
+}
+
+// Generates the SELECT sql only selecting the passed in column and uses Exec#ScanValsContext to scan the result into a
+// slice of primitive values.
+//
+// i: A slice of primitive values
+//
+// col: The column to select when generative the SQL
+func (sd *SelectDataset) PluckContext(ctx context.Context, i interface{}, col string) error {
+ return sd.Select(col).ScanValsContext(ctx, i)
+}
+
+func (sd *SelectDataset) selectSQLBuilder() sb.SQLBuilder {
+ buf := sb.NewSQLBuilder(sd.isPrepared)
+ sd.dialect.ToSelectSQL(buf, sd.GetClauses())
+ return buf
+}
diff --git a/dataset_sql_example_test.go b/select_dataset_example_test.go
similarity index 62%
rename from dataset_sql_example_test.go
rename to select_dataset_example_test.go
index 23e79a17..43ed9867 100644
--- a/dataset_sql_example_test.go
+++ b/select_dataset_example_test.go
@@ -1,13 +1,58 @@
package goqu_test
import (
+ goSQL "database/sql"
"fmt"
+ "os"
"regexp"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
+ "github.com/lib/pq"
)
-func ExampleDataset() {
+const schema = `
+ DROP TABLE IF EXISTS "goqu_user";
+ CREATE TABLE "goqu_user" (
+ "id" SERIAL PRIMARY KEY NOT NULL,
+ "first_name" VARCHAR(45) NOT NULL,
+ "last_name" VARCHAR(45) NOT NULL,
+ "created" TIMESTAMP NOT NULL DEFAULT now()
+ );
+ INSERT INTO "goqu_user" ("first_name", "last_name") VALUES
+ ('Bob', 'Yukon'),
+ ('Sally', 'Yukon'),
+ ('Vinita', 'Yukon'),
+ ('John', 'Doe')
+ `
+
+const defaultDbURI = "postgres://postgres:@localhost:5435/goqupostgres?sslmode=disable"
+
+var goquDb *goqu.Database
+
+func getDb() *goqu.Database {
+ if goquDb == nil {
+ dbURI := os.Getenv("PG_URI")
+ if dbURI == "" {
+ dbURI = defaultDbURI
+ }
+ uri, err := pq.ParseURL(dbURI)
+ if err != nil {
+ panic(err)
+ }
+ pdb, err := goSQL.Open("postgres", uri)
+ if err != nil {
+ panic(err)
+ }
+ goquDb = goqu.New("postgres", pdb)
+ }
+ // reset the db
+ if _, err := goquDb.Exec(schema); err != nil {
+ panic(err)
+ }
+ return goquDb
+}
+
+func ExampleSelectDataset() {
ds := goqu.From("test").
Select(goqu.COUNT("*")).
InnerJoin(goqu.T("test2"), goqu.On(goqu.I("test.fkey").Eq(goqu.I("test2.id")))).
@@ -40,33 +85,30 @@ func ExampleDataset() {
// SELECT COUNT(*) FROM "test" INNER JOIN "test2" ON ("test"."fkey" = "test2"."id") LEFT JOIN "test3" ON ("test2"."fkey" = "test3"."id") WHERE ((("test"."name" ~ ?) AND ("test2"."amount" IS NOT NULL)) AND (("test3"."id" IS NULL) OR ("test3"."status" IN (?, ?, ?)))) GROUP BY "test"."user_id" HAVING (AVG("test3"."age") > ?) ORDER BY "test"."created" DESC NULLS LAST [^(a|b) passed active registered 10]
}
-func ExampleDataset_As() {
- ds := goqu.From("test").As("t")
- sql, _, _ := goqu.From(ds).ToSQL()
+func ExampleSelect() {
+ sql, _, _ := goqu.Select(goqu.L("NOW()")).ToSQL()
fmt.Println(sql)
- // Output: SELECT * FROM (SELECT * FROM "test") AS "t"
+
+ // Output:
+ // SELECT NOW()
}
-func ExampleDataset_Returning() {
- sql, _, _ := goqu.From("test").
- Returning("id").
- ToInsertSQL(goqu.Record{"a": "a", "b": "b"})
- fmt.Println(sql)
- sql, _, _ = goqu.From("test").
- Returning(goqu.T("test").All()).
- ToInsertSQL(goqu.Record{"a": "a", "b": "b"})
- fmt.Println(sql)
- sql, _, _ = goqu.From("test").
- Returning("a", "b").
- ToInsertSQL(goqu.Record{"a": "a", "b": "b"})
- fmt.Println(sql)
+func ExampleFrom() {
+ sql, args, _ := goqu.From("test").ToSQL()
+ fmt.Println(sql, args)
+
// Output:
- // INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "id"
- // INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "test".*
- // INSERT INTO "test" ("a", "b") VALUES ('a', 'b') RETURNING "a", "b"
+ // SELECT * FROM "test" []
+}
+
+func ExampleSelectDataset_As() {
+ ds := goqu.From("test").As("t")
+ sql, _, _ := goqu.From(ds).ToSQL()
+ fmt.Println(sql)
+ // Output: SELECT * FROM (SELECT * FROM "test") AS "t"
}
-func ExampleDataset_Union() {
+func ExampleSelectDataset_Union() {
sql, _, _ := goqu.From("test").
Union(goqu.From("test2")).
ToSQL()
@@ -90,7 +132,7 @@ func ExampleDataset_Union() {
// SELECT * FROM (SELECT * FROM "test" LIMIT 1) AS "t1" UNION (SELECT * FROM (SELECT * FROM "test2" ORDER BY "id" DESC) AS "t1")
}
-func ExampleDataset_UnionAll() {
+func ExampleSelectDataset_UnionAll() {
sql, _, _ := goqu.From("test").
UnionAll(goqu.From("test2")).
ToSQL()
@@ -112,7 +154,7 @@ func ExampleDataset_UnionAll() {
// SELECT * FROM (SELECT * FROM "test" LIMIT 1) AS "t1" UNION ALL (SELECT * FROM (SELECT * FROM "test2" ORDER BY "id" DESC) AS "t1")
}
-func ExampleDataset_With() {
+func ExampleSelectDataset_With() {
sql, _, _ := goqu.From("one").
With("one", goqu.From().Select(goqu.L("1"))).
Select(goqu.Star()).
@@ -130,31 +172,13 @@ func ExampleDataset_With() {
ToSQL()
fmt.Println(sql)
- sql, _, _ = goqu.From("test").
- With("moved_rows", goqu.From("other").Where(goqu.C("date").Lt(123))).
- ToInsertSQL(goqu.From("moved_rows"))
- fmt.Println(sql)
- sql, _, _ = goqu.From("test").
- With("check_vals(val)", goqu.From().Select(goqu.L("123"))).
- Where(goqu.C("val").Eq(goqu.From("check_vals").Select("val"))).
- ToDeleteSQL()
- fmt.Println(sql)
- sql, _, _ = goqu.From("test").
- With("some_vals(val)", goqu.From().Select(goqu.L("123"))).
- Where(goqu.C("val").Eq(goqu.From("some_vals").Select("val"))).
- ToUpdateSQL(goqu.Record{"name": "Test"})
- fmt.Println(sql)
-
// Output:
// WITH one AS (SELECT 1) SELECT * FROM "one"
// WITH intermed AS (SELECT * FROM "test" WHERE ("x" >= 5)), derived AS (SELECT * FROM "intermed" WHERE ("x" < 10)) SELECT * FROM "derived"
// WITH multi(x,y) AS (SELECT 1, 2) SELECT "x", "y" FROM "multi"
- // WITH moved_rows AS (SELECT * FROM "other" WHERE ("date" < 123)) INSERT INTO "test" SELECT * FROM "moved_rows"
- // WITH check_vals(val) AS (SELECT 123) DELETE FROM "test" WHERE ("val" IN (SELECT "val" FROM "check_vals"))
- // WITH some_vals(val) AS (SELECT 123) UPDATE "test" SET "name"='Test' WHERE ("val" IN (SELECT "val" FROM "some_vals"))
}
-func ExampleDataset_WithRecursive() {
+func ExampleSelectDataset_WithRecursive() {
sql, _, _ := goqu.From("nums").
WithRecursive("nums(x)",
goqu.From().Select(goqu.L("1")).
@@ -166,7 +190,7 @@ func ExampleDataset_WithRecursive() {
// WITH RECURSIVE nums(x) AS (SELECT 1 UNION ALL (SELECT x+1 FROM "nums" WHERE ("x" < 5))) SELECT * FROM "nums"
}
-func ExampleDataset_Intersect() {
+func ExampleSelectDataset_Intersect() {
sql, _, _ := goqu.From("test").
Intersect(goqu.From("test2")).
ToSQL()
@@ -188,7 +212,7 @@ func ExampleDataset_Intersect() {
// SELECT * FROM (SELECT * FROM "test" LIMIT 1) AS "t1" INTERSECT (SELECT * FROM (SELECT * FROM "test2" ORDER BY "id" DESC) AS "t1")
}
-func ExampleDataset_IntersectAll() {
+func ExampleSelectDataset_IntersectAll() {
sql, _, _ := goqu.From("test").
IntersectAll(goqu.From("test2")).
ToSQL()
@@ -210,7 +234,7 @@ func ExampleDataset_IntersectAll() {
// SELECT * FROM (SELECT * FROM "test" LIMIT 1) AS "t1" INTERSECT ALL (SELECT * FROM (SELECT * FROM "test2" ORDER BY "id" DESC) AS "t1")
}
-func ExampleDataset_ClearOffset() {
+func ExampleSelectDataset_ClearOffset() {
ds := goqu.From("test").
Offset(2)
sql, _, _ := ds.
@@ -221,16 +245,15 @@ func ExampleDataset_ClearOffset() {
// SELECT * FROM "test"
}
-func ExampleDataset_Offset() {
- ds := goqu.From("test").
- Offset(2)
+func ExampleSelectDataset_Offset() {
+ ds := goqu.From("test").Offset(2)
sql, _, _ := ds.ToSQL()
fmt.Println(sql)
// Output:
// SELECT * FROM "test" OFFSET 2
}
-func ExampleDataset_Limit() {
+func ExampleSelectDataset_Limit() {
ds := goqu.From("test").Limit(10)
sql, _, _ := ds.ToSQL()
fmt.Println(sql)
@@ -238,7 +261,7 @@ func ExampleDataset_Limit() {
// SELECT * FROM "test" LIMIT 10
}
-func ExampleDataset_LimitAll() {
+func ExampleSelectDataset_LimitAll() {
ds := goqu.From("test").LimitAll()
sql, _, _ := ds.ToSQL()
fmt.Println(sql)
@@ -246,7 +269,7 @@ func ExampleDataset_LimitAll() {
// SELECT * FROM "test" LIMIT ALL
}
-func ExampleDataset_ClearLimit() {
+func ExampleSelectDataset_ClearLimit() {
ds := goqu.From("test").Limit(10)
sql, _, _ := ds.ClearLimit().ToSQL()
fmt.Println(sql)
@@ -254,16 +277,15 @@ func ExampleDataset_ClearLimit() {
// SELECT * FROM "test"
}
-func ExampleDataset_Order() {
- ds := goqu.From("test").
- Order(goqu.C("a").Asc())
+func ExampleSelectDataset_Order() {
+ ds := goqu.From("test").Order(goqu.C("a").Asc())
sql, _, _ := ds.ToSQL()
fmt.Println(sql)
// Output:
// SELECT * FROM "test" ORDER BY "a" ASC
}
-func ExampleDataset_OrderAppend() {
+func ExampleSelectDataset_OrderAppend() {
ds := goqu.From("test").Order(goqu.C("a").Asc())
sql, _, _ := ds.OrderAppend(goqu.C("b").Desc().NullsLast()).ToSQL()
fmt.Println(sql)
@@ -271,7 +293,7 @@ func ExampleDataset_OrderAppend() {
// SELECT * FROM "test" ORDER BY "a" ASC, "b" DESC NULLS LAST
}
-func ExampleDataset_OrderPrepend() {
+func ExampleSelectDataset_OrderPrepend() {
ds := goqu.From("test").Order(goqu.C("a").Asc())
sql, _, _ := ds.OrderPrepend(goqu.C("b").Desc().NullsLast()).ToSQL()
fmt.Println(sql)
@@ -279,7 +301,7 @@ func ExampleDataset_OrderPrepend() {
// SELECT * FROM "test" ORDER BY "b" DESC NULLS LAST, "a" ASC
}
-func ExampleDataset_ClearOrder() {
+func ExampleSelectDataset_ClearOrder() {
ds := goqu.From("test").Order(goqu.C("a").Asc())
sql, _, _ := ds.ClearOrder().ToSQL()
fmt.Println(sql)
@@ -287,7 +309,17 @@ func ExampleDataset_ClearOrder() {
// SELECT * FROM "test"
}
-func ExampleDataset_Having() {
+func ExampleSelectDataset_GroupBy() {
+ sql, _, _ := goqu.From("test").
+ Select(goqu.SUM("income").As("income_sum")).
+ GroupBy("age").
+ ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // SELECT SUM("income") AS "income_sum" FROM "test" GROUP BY "age"
+}
+
+func ExampleSelectDataset_Having() {
sql, _, _ := goqu.From("test").Having(goqu.SUM("income").Gt(1000)).ToSQL()
fmt.Println(sql)
sql, _, _ = goqu.From("test").GroupBy("age").Having(goqu.SUM("income").Gt(1000)).ToSQL()
@@ -297,7 +329,7 @@ func ExampleDataset_Having() {
// SELECT * FROM "test" GROUP BY "age" HAVING (SUM("income") > 1000)
}
-func ExampleDataset_Where() {
+func ExampleSelectDataset_Where() {
// By default everything is anded together
sql, _, _ := goqu.From("test").Where(goqu.Ex{
"a": goqu.Op{"gt": 10},
@@ -355,7 +387,7 @@ func ExampleDataset_Where() {
// SELECT * FROM "test" WHERE (("a" > 10) OR (("b" < 10) AND ("c" IS NULL)))
}
-func ExampleDataset_Where_prepared() {
+func ExampleSelectDataset_Where_prepared() {
// By default everything is anded together
sql, args, _ := goqu.From("test").Prepared(true).Where(goqu.Ex{
"a": goqu.Op{"gt": 10},
@@ -413,7 +445,7 @@ func ExampleDataset_Where_prepared() {
// SELECT * FROM "test" WHERE (("a" > ?) OR (("b" < ?) AND ("c" IS NULL))) [10 10]
}
-func ExampleDataset_ClearWhere() {
+func ExampleSelectDataset_ClearWhere() {
ds := goqu.From("test").Where(
goqu.Or(
goqu.C("a").Gt(10),
@@ -429,7 +461,7 @@ func ExampleDataset_ClearWhere() {
// SELECT * FROM "test"
}
-func ExampleDataset_Join() {
+func ExampleSelectDataset_Join() {
sql, _, _ := goqu.From("test").Join(
goqu.T("test2"),
goqu.On(goqu.Ex{"test.fkey": goqu.I("test2.Id")}),
@@ -458,7 +490,7 @@ func ExampleDataset_Join() {
}
-func ExampleDataset_InnerJoin() {
+func ExampleSelectDataset_InnerJoin() {
sql, _, _ := goqu.From("test").InnerJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -491,7 +523,7 @@ func ExampleDataset_InnerJoin() {
// SELECT * FROM "test" INNER JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_FullOuterJoin() {
+func ExampleSelectDataset_FullOuterJoin() {
sql, _, _ := goqu.From("test").FullOuterJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -524,7 +556,7 @@ func ExampleDataset_FullOuterJoin() {
// SELECT * FROM "test" FULL OUTER JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_RightOuterJoin() {
+func ExampleSelectDataset_RightOuterJoin() {
sql, _, _ := goqu.From("test").RightOuterJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -557,7 +589,7 @@ func ExampleDataset_RightOuterJoin() {
// SELECT * FROM "test" RIGHT OUTER JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_LeftOuterJoin() {
+func ExampleSelectDataset_LeftOuterJoin() {
sql, _, _ := goqu.From("test").LeftOuterJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -590,7 +622,7 @@ func ExampleDataset_LeftOuterJoin() {
// SELECT * FROM "test" LEFT OUTER JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_FullJoin() {
+func ExampleSelectDataset_FullJoin() {
sql, _, _ := goqu.From("test").FullJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -623,7 +655,7 @@ func ExampleDataset_FullJoin() {
// SELECT * FROM "test" FULL JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_RightJoin() {
+func ExampleSelectDataset_RightJoin() {
sql, _, _ := goqu.From("test").RightJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -656,7 +688,7 @@ func ExampleDataset_RightJoin() {
// SELECT * FROM "test" RIGHT JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_LeftJoin() {
+func ExampleSelectDataset_LeftJoin() {
sql, _, _ := goqu.From("test").LeftJoin(
goqu.T("test2"),
goqu.On(goqu.Ex{
@@ -689,7 +721,7 @@ func ExampleDataset_LeftJoin() {
// SELECT * FROM "test" LEFT JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t" ON ("test"."fkey" = "t"."Id")
}
-func ExampleDataset_NaturalJoin() {
+func ExampleSelectDataset_NaturalJoin() {
sql, _, _ := goqu.From("test").NaturalJoin(goqu.T("test2")).ToSQL()
fmt.Println(sql)
@@ -708,7 +740,7 @@ func ExampleDataset_NaturalJoin() {
// SELECT * FROM "test" NATURAL JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t"
}
-func ExampleDataset_NaturalLeftJoin() {
+func ExampleSelectDataset_NaturalLeftJoin() {
sql, _, _ := goqu.From("test").NaturalLeftJoin(goqu.T("test2")).ToSQL()
fmt.Println(sql)
@@ -727,7 +759,7 @@ func ExampleDataset_NaturalLeftJoin() {
// SELECT * FROM "test" NATURAL LEFT JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t"
}
-func ExampleDataset_NaturalRightJoin() {
+func ExampleSelectDataset_NaturalRightJoin() {
sql, _, _ := goqu.From("test").NaturalRightJoin(goqu.T("test2")).ToSQL()
fmt.Println(sql)
@@ -746,7 +778,7 @@ func ExampleDataset_NaturalRightJoin() {
// SELECT * FROM "test" NATURAL RIGHT JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t"
}
-func ExampleDataset_NaturalFullJoin() {
+func ExampleSelectDataset_NaturalFullJoin() {
sql, _, _ := goqu.From("test").NaturalFullJoin(goqu.T("test2")).ToSQL()
fmt.Println(sql)
@@ -765,7 +797,7 @@ func ExampleDataset_NaturalFullJoin() {
// SELECT * FROM "test" NATURAL FULL JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t"
}
-func ExampleDataset_CrossJoin() {
+func ExampleSelectDataset_CrossJoin() {
sql, _, _ := goqu.From("test").CrossJoin(goqu.T("test2")).ToSQL()
fmt.Println(sql)
@@ -784,7 +816,7 @@ func ExampleDataset_CrossJoin() {
// SELECT * FROM "test" CROSS JOIN (SELECT * FROM "test2" WHERE ("amount" > 0)) AS "t"
}
-func ExampleDataset_FromSelf() {
+func ExampleSelectDataset_FromSelf() {
sql, _, _ := goqu.From("test").FromSelf().ToSQL()
fmt.Println(sql)
sql, _, _ = goqu.From("test").As("my_test_table").FromSelf().ToSQL()
@@ -794,7 +826,7 @@ func ExampleDataset_FromSelf() {
// SELECT * FROM (SELECT * FROM "test") AS "my_test_table"
}
-func ExampleDataset_From() {
+func ExampleSelectDataset_From() {
ds := goqu.From("test")
sql, _, _ := ds.From("test2").ToSQL()
fmt.Println(sql)
@@ -802,7 +834,7 @@ func ExampleDataset_From() {
// SELECT * FROM "test2"
}
-func ExampleDataset_From_withDataset() {
+func ExampleSelectDataset_From_withDataset() {
ds := goqu.From("test")
fromDs := ds.Where(goqu.C("age").Gt(10))
sql, _, _ := ds.From(fromDs).ToSQL()
@@ -811,7 +843,7 @@ func ExampleDataset_From_withDataset() {
// SELECT * FROM (SELECT * FROM "test" WHERE ("age" > 10)) AS "t1"
}
-func ExampleDataset_From_withAliasedDataset() {
+func ExampleSelectDataset_From_withAliasedDataset() {
ds := goqu.From("test")
fromDs := ds.Where(goqu.C("age").Gt(10))
sql, _, _ := ds.From(fromDs.As("test2")).ToSQL()
@@ -820,14 +852,14 @@ func ExampleDataset_From_withAliasedDataset() {
// SELECT * FROM (SELECT * FROM "test" WHERE ("age" > 10)) AS "test2"
}
-func ExampleDataset_Select() {
+func ExampleSelectDataset_Select() {
sql, _, _ := goqu.From("test").Select("a", "b", "c").ToSQL()
fmt.Println(sql)
// Output:
// SELECT "a", "b", "c" FROM "test"
}
-func ExampleDataset_Select_withDataset() {
+func ExampleSelectDataset_Select_withDataset() {
ds := goqu.From("test")
fromDs := ds.Select("age").Where(goqu.C("age").Gt(10))
sql, _, _ := ds.From().Select(fromDs).ToSQL()
@@ -836,7 +868,7 @@ func ExampleDataset_Select_withDataset() {
// SELECT (SELECT "age" FROM "test" WHERE ("age" > 10))
}
-func ExampleDataset_Select_withAliasedDataset() {
+func ExampleSelectDataset_Select_withAliasedDataset() {
ds := goqu.From("test")
fromDs := ds.Select("age").Where(goqu.C("age").Gt(10))
sql, _, _ := ds.From().Select(fromDs.As("ages")).ToSQL()
@@ -845,14 +877,14 @@ func ExampleDataset_Select_withAliasedDataset() {
// SELECT (SELECT "age" FROM "test" WHERE ("age" > 10)) AS "ages"
}
-func ExampleDataset_Select_withLiteral() {
+func ExampleSelectDataset_Select_withLiteral() {
sql, _, _ := goqu.From("test").Select(goqu.L("a + b").As("sum")).ToSQL()
fmt.Println(sql)
// Output:
// SELECT a + b AS "sum" FROM "test"
}
-func ExampleDataset_Select_withSQLFunctionExpression() {
+func ExampleSelectDataset_Select_withSQLFunctionExpression() {
sql, _, _ := goqu.From("test").Select(
goqu.COUNT("*").As("age_count"),
goqu.MAX("age").As("max_age"),
@@ -863,7 +895,7 @@ func ExampleDataset_Select_withSQLFunctionExpression() {
// SELECT COUNT(*) AS "age_count", MAX("age") AS "max_age", AVG("age") AS "avg_age" FROM "test"
}
-func ExampleDataset_Select_withStruct() {
+func ExampleSelectDataset_Select_withStruct() {
ds := goqu.From("test")
type myStruct struct {
@@ -907,14 +939,14 @@ func ExampleDataset_Select_withStruct() {
// SELECT "address", "email_address", "name" FROM "test"
}
-func ExampleDataset_SelectDistinct() {
+func ExampleSelectDataset_SelectDistinct() {
sql, _, _ := goqu.From("test").SelectDistinct("a", "b").ToSQL()
fmt.Println(sql)
// Output:
// SELECT DISTINCT "a", "b" FROM "test"
}
-func ExampleDataset_SelectAppend() {
+func ExampleSelectDataset_SelectAppend() {
ds := goqu.From("test").Select("a", "b")
sql, _, _ := ds.SelectAppend("c").ToSQL()
fmt.Println(sql)
@@ -926,7 +958,7 @@ func ExampleDataset_SelectAppend() {
// SELECT DISTINCT "a", "b", "c" FROM "test"
}
-func ExampleDataset_ClearSelect() {
+func ExampleSelectDataset_ClearSelect() {
ds := goqu.From("test").Select("a", "b")
sql, _, _ := ds.ClearSelect().ToSQL()
fmt.Println(sql)
@@ -938,38 +970,38 @@ func ExampleDataset_ClearSelect() {
// SELECT * FROM "test"
}
-func ExampleDataset_ToSQL() {
+func ExampleSelectDataset_ToSQL() {
sql, args, _ := goqu.From("items").Where(goqu.Ex{"a": 1}).ToSQL()
fmt.Println(sql, args)
// Output:
// SELECT * FROM "items" WHERE ("a" = 1) []
}
-func ExampleDataset_ToSQL_prepared() {
+func ExampleSelectDataset_ToSQL_prepared() {
sql, args, _ := goqu.From("items").Where(goqu.Ex{"a": 1}).Prepared(true).ToSQL()
fmt.Println(sql, args)
// Output:
// SELECT * FROM "items" WHERE ("a" = ?) [1]
}
-func ExampleDataset_ToUpdateSQL() {
+func ExampleSelectDataset_Update() {
type item struct {
Address string `db:"address"`
Name string `db:"name"`
}
- sql, args, _ := goqu.From("items").ToUpdateSQL(
+ sql, args, _ := goqu.From("items").Update().Set(
item{Name: "Test", Address: "111 Test Addr"},
- )
+ ).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").ToUpdateSQL(
+ sql, args, _ = goqu.From("items").Update().Set(
goqu.Record{"name": "Test", "address": "111 Test Addr"},
- )
+ ).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").ToUpdateSQL(
+ sql, args, _ = goqu.From("items").Update().Set(
map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
- )
+ ).ToSQL()
fmt.Println(sql, args)
// Output:
@@ -978,90 +1010,36 @@ func ExampleDataset_ToUpdateSQL() {
// UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
}
-func ExampleDataset_ToUpdateSQL_withSkipUpdateTag() {
- type item struct {
- Address string `db:"address"`
- Name string `db:"name" goqu:"skipupdate"`
- }
- sql, args, _ := goqu.From("items").ToUpdateSQL(
- item{Name: "Test", Address: "111 Test Addr"},
- )
- fmt.Println(sql, args)
-
- // Output:
- // UPDATE "items" SET "address"='111 Test Addr' []
-}
-
-func ExampleDataset_ToUpdateSQL_withNoTags() {
- type item struct {
- Address string
- Name string
- }
- sql, args, _ := goqu.From("items").ToUpdateSQL(
- item{Name: "Test", Address: "111 Test Addr"},
- )
- fmt.Println(sql, args)
-
- // Output:
- // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
-}
-
-func ExampleDataset_ToUpdateSQL_prepared() {
- type item struct {
- Address string `db:"address"`
- Name string `db:"name"`
- }
-
- sql, args, _ := goqu.From("items").Prepared(true).ToUpdateSQL(
- item{Name: "Test", Address: "111 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").Prepared(true).ToUpdateSQL(
- goqu.Record{"name": "Test", "address": "111 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").Prepared(true).ToUpdateSQL(
- map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
- )
- fmt.Println(sql, args)
- // Output:
- // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
- // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
- // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
-}
-
-func ExampleDataset_ToInsertSQL() {
+func ExampleSelectDataset_Insert() {
type item struct {
ID uint32 `db:"id" goqu:"skipinsert"`
Address string `db:"address"`
Name string `db:"name"`
}
- sql, args, _ := goqu.From("items").ToInsertSQL(
+ sql, args, _ := goqu.From("items").Insert().Rows(
item{Name: "Test1", Address: "111 Test Addr"},
item{Name: "Test2", Address: "112 Test Addr"},
- )
+ ).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").ToInsertSQL(
+ sql, args, _ = goqu.From("items").Insert().Rows(
goqu.Record{"name": "Test1", "address": "111 Test Addr"},
goqu.Record{"name": "Test2", "address": "112 Test Addr"},
- )
+ ).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").ToInsertSQL(
+ sql, args, _ = goqu.From("items").Insert().Rows(
[]item{
{Name: "Test1", Address: "111 Test Addr"},
{Name: "Test2", Address: "112 Test Addr"},
- })
+ }).ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").ToInsertSQL(
+ sql, args, _ = goqu.From("items").Insert().Rows(
[]goqu.Record{
{"name": "Test1", "address": "111 Test Addr"},
{"name": "Test2", "address": "112 Test Addr"},
- })
+ }).ToSQL()
fmt.Println(sql, args)
// Output:
// INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
@@ -1070,377 +1048,178 @@ func ExampleDataset_ToInsertSQL() {
// INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
}
-func ExampleDataset_ToInsertSQL_withNoDbTag() {
- type item struct {
- ID uint32 `goqu:"skipinsert"`
- Address string
- Name string
- }
- sql, args, _ := goqu.From("items").ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
- // Output:
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') []
-}
-
-func ExampleDataset_ToInsertSQL_withGoquSkipInsertTag() {
- type item struct {
- ID uint32 `goqu:"skipinsert"`
- Address string `goqu:"skipinsert"`
- Name string
- }
- sql, args, _ := goqu.From("items").ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
- // Output:
- // INSERT INTO "items" ("name") VALUES ('Test1'), ('Test2') []
-}
-
-func ExampleDataset_ToInsertSQL_prepared() {
- type item struct {
- ID uint32 `db:"id" goqu:"skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
- }
-
- sql, args, _ := goqu.From("items").Prepared(true).ToInsertSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").Prepared(true).ToInsertSQL(
- goqu.Record{"name": "Test1", "address": "111 Test Addr"},
- goqu.Record{"name": "Test2", "address": "112 Test Addr"},
- )
+func ExampleSelectDataset_Delete() {
+ sql, args, _ := goqu.From("items").Delete().ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").Prepared(true).ToInsertSQL(
- []item{
- {Name: "Test1", Address: "111 Test Addr"},
- {Name: "Test2", Address: "112 Test Addr"},
- })
+ sql, args, _ = goqu.From("items").
+ Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
+ Delete().
+ ToSQL()
fmt.Println(sql, args)
- sql, args, _ = goqu.From("items").Prepared(true).ToInsertSQL(
- []goqu.Record{
- {"name": "Test1", "address": "111 Test Addr"},
- {"name": "Test2", "address": "112 Test Addr"},
- })
- fmt.Println(sql, args)
// Output:
- // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
- // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
- // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
- // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
+ // DELETE FROM "items" []
+ // DELETE FROM "items" WHERE ("id" > 10) []
}
-func ExampleDataset_ToInsertIgnoreSQL() {
- type item struct {
- ID uint32 `db:"id" goqu:"skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
- }
- sql, args, _ := goqu.From("items").ToInsertIgnoreSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").ToInsertIgnoreSQL(
- goqu.Record{"name": "Test1", "address": "111 Test Addr"},
- goqu.Record{"name": "Test2", "address": "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").ToInsertIgnoreSQL(
- []item{
- {Name: "Test1", Address: "111 Test Addr"},
- {Name: "Test2", Address: "112 Test Addr"},
- })
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").ToInsertIgnoreSQL(
- []goqu.Record{
- {"name": "Test1", "address": "111 Test Addr"},
- {"name": "Test2", "address": "112 Test Addr"},
- })
+func ExampleSelectDataset_Truncate() {
+ sql, args, _ := goqu.From("items").Truncate().ToSQL()
fmt.Println(sql, args)
// Output:
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
+ // TRUNCATE "items" []
}
-func ExampleDataset_ToInsertIgnoreSQL_withNoDBTag() {
- type item struct {
- ID uint32 `goqu:"skipinsert"`
- Address string
- Name string
- }
- sql, args, _ := goqu.From("items").ToInsertIgnoreSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
+func ExampleSelectDataset_Prepared() {
+ sql, args, _ := goqu.From("items").Prepared(true).Where(goqu.Ex{
+ "col1": "a",
+ "col2": 1,
+ "col3": true,
+ "col4": false,
+ "col5": []string{"a", "b", "c"},
+ }).ToSQL()
fmt.Println(sql, args)
+ // nolint:lll
// Output:
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
+ // SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IN (?, ?, ?))) [a 1 a b c]
}
-func ExampleDataset_ToInsertIgnoreSQL_withGoquSkipInsertTag() {
- type item struct {
- ID uint32 `goqu:"skipinsert"`
- Address string
- Name string `goqu:"skipinsert"`
+func ExampleSelectDataset_ScanStructs() {
+ type User struct {
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
}
- sql, args, _ := goqu.From("items").ToInsertIgnoreSQL(
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
- // Output:
- // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') ON CONFLICT DO NOTHING []
-}
-
-func ExampleDataset_ToInsertConflictSQL() {
- type item struct {
- ID uint32 `db:"id" goqu:"skipinsert"`
- Address string `db:"address"`
- Name string `db:"name"`
+ db := getDb()
+ var users []User
+ if err := db.From("goqu_user").ScanStructs(&users); err != nil {
+ fmt.Println(err.Error())
+ return
}
- sql, args, _ := goqu.From("items").ToInsertConflictSQL(
- goqu.DoNothing(),
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").ToInsertConflictSQL(
- goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}),
- goqu.Record{"name": "Test1", "address": "111 Test Addr"},
- goqu.Record{"name": "Test2", "address": "112 Test Addr"},
- )
- fmt.Println(sql, args)
+ fmt.Printf("\n%+v", users)
- sql, args, _ = goqu.From("items").ToInsertConflictSQL(
- goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}).Where(goqu.C("allow_update").IsTrue()),
- []item{
- {Name: "Test1", Address: "111 Test Addr"},
- {Name: "Test2", Address: "112 Test Addr"},
- })
- fmt.Println(sql, args)
+ users = users[0:0]
+ if err := db.From("goqu_user").Select("first_name").ScanStructs(&users); err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Printf("\n%+v", users)
- // nolint:lll
// Output:
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() WHERE ("allow_update" IS TRUE) []
+ // [{FirstName:Bob LastName:Yukon} {FirstName:Sally LastName:Yukon} {FirstName:Vinita LastName:Yukon} {FirstName:John LastName:Doe}]
+ // [{FirstName:Bob LastName:} {FirstName:Sally LastName:} {FirstName:Vinita LastName:} {FirstName:John LastName:}]
}
-func ExampleDataset_ToInsertConflictSQL_withNoDbTag() {
- type item struct {
- ID uint32 `goqu:"skipinsert"`
- Address string
- Name string
+func ExampleSelectDataset_ScanStructs_prepared() {
+ type User struct {
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
}
- sql, args, _ := goqu.From("items").ToInsertConflictSQL(
- goqu.DoNothing(),
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").ToInsertConflictSQL(
- goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}),
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
+ db := getDb()
- sql, args, _ = goqu.From("items").ToInsertConflictSQL(
- goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}).Where(goqu.C("allow_update").IsTrue()),
- []item{
- {Name: "Test1", Address: "111 Test Addr"},
- {Name: "Test2", Address: "112 Test Addr"},
+ ds := db.From("goqu_user").
+ Prepared(true).
+ Where(goqu.Ex{
+ "last_name": "Yukon",
})
- fmt.Println(sql, args)
- // nolint:lll
- // Output:
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT DO NOTHING []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() []
- // INSERT INTO "items" ("address", "name") VALUES ('111 Test Addr', 'Test1'), ('112 Test Addr', 'Test2') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() WHERE ("allow_update" IS TRUE) []
-}
-
-func ExampleDataset_ToInsertConflictSQL_withGoquSkipInsertTag() {
- type item struct {
- ID uint32 `goqu:"skipinsert"`
- Address string
- Name string `goqu:"skipinsert"`
+ var users []User
+ if err := ds.ScanStructs(&users); err != nil {
+ fmt.Println(err.Error())
+ return
}
- sql, args, _ := goqu.From("items").ToInsertConflictSQL(
- goqu.DoNothing(),
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").ToInsertConflictSQL(
- goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}),
- item{Name: "Test1", Address: "111 Test Addr"},
- item{Name: "Test2", Address: "112 Test Addr"},
- )
- fmt.Println(sql, args)
+ fmt.Printf("\n%+v", users)
- sql, args, _ = goqu.From("items").ToInsertConflictSQL(
- goqu.DoUpdate("key", goqu.Record{"updated": goqu.L("NOW()")}).Where(goqu.C("allow_update").IsTrue()),
- []item{
- {Name: "Test1", Address: "111 Test Addr"},
- {Name: "Test2", Address: "112 Test Addr"},
- })
- fmt.Println(sql, args)
-
- // nolint:lll
// Output:
- // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') ON CONFLICT DO NOTHING []
- // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() []
- // INSERT INTO "items" ("address") VALUES ('111 Test Addr'), ('112 Test Addr') ON CONFLICT (key) DO UPDATE SET "updated"=NOW() WHERE ("allow_update" IS TRUE) []
+ // [{FirstName:Bob LastName:Yukon} {FirstName:Sally LastName:Yukon} {FirstName:Vinita LastName:Yukon}]
}
-func ExampleDataset_ToDeleteSQL() {
- sql, args, _ := goqu.From("items").ToDeleteSQL()
- fmt.Println(sql, args)
+func ExampleSelectDataset_ScanStruct() {
+ type User struct {
+ FirstName string `db:"first_name"`
+ LastName string `db:"last_name"`
+ }
+ db := getDb()
+ findUserByName := func(name string) {
+ var user User
+ ds := db.From("goqu_user").Where(goqu.C("first_name").Eq(name))
+ found, err := ds.ScanStruct(&user)
+ switch {
+ case err != nil:
+ fmt.Println(err.Error())
+ case !found:
+ fmt.Printf("No user found for first_name %s\n", name)
+ default:
+ fmt.Printf("Found user: %+v\n", user)
+ }
+ }
- sql, args, _ = goqu.From("items").
- Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
- ToDeleteSQL()
- fmt.Println(sql, args)
+ findUserByName("Bob")
+ findUserByName("Zeb")
// Output:
- // DELETE FROM "items" []
- // DELETE FROM "items" WHERE ("id" > 10) []
+ // Found user: {FirstName:Bob LastName:Yukon}
+ // No user found for first_name Zeb
}
-func ExampleDataset_ToDeleteSQL_prepared() {
- sql, args, _ := goqu.From("items").Prepared(true).ToDeleteSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").
- Prepared(true).
- Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
- ToDeleteSQL()
- fmt.Println(sql, args)
+func ExampleSelectDataset_ScanVals() {
+ var ids []int64
+ if err := getDb().From("goqu_user").Select("id").ScanVals(&ids); err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Printf("UserIds = %+v", ids)
// Output:
- // DELETE FROM "items" []
- // DELETE FROM "items" WHERE ("id" > ?) [10]
+ // UserIds = [1 2 3 4]
}
-func ExampleDataset_ToDeleteSQL_withWhere() {
- sql, args, _ := goqu.From("items").Where(goqu.C("id").IsNotNull()).ToDeleteSQL()
- fmt.Println(sql, args)
+func ExampleSelectDataset_ScanVal() {
- // Output:
- // DELETE FROM "items" WHERE ("id" IS NOT NULL) []
-}
+ db := getDb()
+ findUserIDByName := func(name string) {
+ var id int64
+ ds := db.From("goqu_user").
+ Select("id").
+ Where(goqu.C("first_name").Eq(name))
-func ExampleDataset_ToDeleteSQL_withReturning() {
- ds := goqu.From("items")
- sql, args, _ := ds.Returning("id").ToDeleteSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = ds.Returning("id").Where(goqu.C("id").IsNotNull()).ToDeleteSQL()
- fmt.Println(sql, args)
+ found, err := ds.ScanVal(&id)
+ switch {
+ case err != nil:
+ fmt.Println(err.Error())
+ case !found:
+ fmt.Printf("No id found for user %s", name)
+ default:
+ fmt.Printf("\nFound userId: %+v\n", id)
+ }
+ }
+ findUserIDByName("Bob")
+ findUserIDByName("Zeb")
// Output:
- // DELETE FROM "items" RETURNING "id" []
- // DELETE FROM "items" WHERE ("id" IS NOT NULL) RETURNING "id" []
+ // Found userId: 1
+ // No id found for user Zeb
}
-func ExampleDataset_ToTruncateSQL() {
- sql, args, _ := goqu.From("items").ToTruncateSQL()
- fmt.Println(sql, args)
- // Output:
- // TRUNCATE "items" []
-}
+func ExampleSelectDataset_Count() {
-func ExampleDataset_ToTruncateWithOptsSQL() {
- sql, _, _ := goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Cascade: true})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Restrict: true})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Identity: "RESTART"})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Identity: "RESTART", Cascade: true})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Identity: "RESTART", Restrict: true})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Identity: "CONTINUE"})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Identity: "CONTINUE", Cascade: true})
- fmt.Println(sql)
- sql, _, _ = goqu.From("items").
- ToTruncateWithOptsSQL(goqu.TruncateOptions{Identity: "CONTINUE", Restrict: true})
- fmt.Println(sql)
+ if count, err := getDb().From("goqu_user").Count(); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Printf("\nCount:= %d", count)
+ }
// Output:
- // TRUNCATE "items"
- // TRUNCATE "items" CASCADE
- // TRUNCATE "items" RESTRICT
- // TRUNCATE "items" RESTART IDENTITY
- // TRUNCATE "items" RESTART IDENTITY CASCADE
- // TRUNCATE "items" RESTART IDENTITY RESTRICT
- // TRUNCATE "items" CONTINUE IDENTITY
- // TRUNCATE "items" CONTINUE IDENTITY CASCADE
- // TRUNCATE "items" CONTINUE IDENTITY RESTRICT
-}
-
-func ExampleDataset_Prepared() {
- sql, args, _ := goqu.From("items").Prepared(true).Where(goqu.Ex{
- "col1": "a",
- "col2": 1,
- "col3": true,
- "col4": false,
- "col5": []string{"a", "b", "c"},
- }).ToSQL()
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").Prepared(true).ToInsertSQL(
- goqu.Record{"name": "Test1", "address": "111 Test Addr"},
- goqu.Record{"name": "Test2", "address": "112 Test Addr"},
- )
- fmt.Println(sql, args)
-
- sql, args, _ = goqu.From("items").Prepared(true).ToUpdateSQL(
- goqu.Record{"name": "Test", "address": "111 Test Addr"},
- )
- fmt.Println(sql, args)
+ // Count:= 4
+}
- sql, args, _ = goqu.From("items").
- Prepared(true).
- Where(goqu.Ex{"id": goqu.Op{"gt": 10}}).
- ToDeleteSQL()
- fmt.Println(sql, args)
+func ExampleSelectDataset_Pluck() {
+ var lastNames []string
+ if err := getDb().From("goqu_user").Pluck(&lastNames, "last_name"); err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Printf("LastNames := %+v", lastNames)
- // nolint:lll
// Output:
- // SELECT * FROM "items" WHERE (("col1" = ?) AND ("col2" = ?) AND ("col3" IS TRUE) AND ("col4" IS FALSE) AND ("col5" IN (?, ?, ?))) [a 1 a b c]
- // INSERT INTO "items" ("address", "name") VALUES (?, ?), (?, ?) [111 Test Addr Test1 112 Test Addr Test2]
- // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
- // DELETE FROM "items" WHERE ("id" > ?) [10]
+ // LastNames := [Yukon Yukon Yukon Doe]
}
diff --git a/select_dataset_test.go b/select_dataset_test.go
new file mode 100644
index 00000000..9b64e89b
--- /dev/null
+++ b/select_dataset_test.go
@@ -0,0 +1,2310 @@
+package goqu
+
+import (
+ "testing"
+
+ "github.com/DATA-DOG/go-sqlmock"
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+ "github.com/doug-martin/goqu/v8/mocks"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+)
+
+type dsTestActionItem struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+}
+
+type selectDatasetSuite struct {
+ suite.Suite
+}
+
+func (sds *selectDatasetSuite) TestClone() {
+ t := sds.T()
+ ds := From("test")
+ assert.Equal(t, ds.Clone(), ds)
+}
+
+func (sds *selectDatasetSuite) TestExpression() {
+ t := sds.T()
+ ds := From("test")
+ assert.Equal(t, ds.Expression(), ds)
+}
+
+func (sds *selectDatasetSuite) TestDialect() {
+ t := sds.T()
+ ds := From("test")
+ assert.NotNil(t, ds.Dialect())
+}
+
+func (sds *selectDatasetSuite) TestWithDialect() {
+ t := sds.T()
+ ds := From("test")
+ dialect := GetDialect("default")
+ ds.WithDialect("default")
+ assert.Equal(t, ds.Dialect(), dialect)
+}
+
+func (sds *selectDatasetSuite) TestPrepared() {
+ t := sds.T()
+ ds := From("test")
+ preparedDs := ds.Prepared(true)
+ assert.True(t, preparedDs.IsPrepared())
+ assert.False(t, ds.IsPrepared())
+ // should apply the prepared to any datasets created from the root
+ assert.True(t, preparedDs.Where(Ex{"a": 1}).IsPrepared())
+}
+
+func (sds *selectDatasetSuite) TestGetClauses() {
+ t := sds.T()
+ ds := From("test")
+ ce := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression(I("test")))
+ assert.Equal(t, ce, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestWith() {
+ t := sds.T()
+ from := From("cte")
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from))
+ assert.Equal(t, ec, ds.With("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestWithRecursive() {
+ t := sds.T()
+ from := From("cte")
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from))
+ assert.Equal(t, ec, ds.WithRecursive("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestSelect() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetSelect(exp.NewColumnListExpression(C("a")))
+ assert.Equal(t, ec, ds.Select(C("a")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestSelect_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ selectSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ selectSQL, _, err = ds1.Select().ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ selectSQL, _, err = ds1.Select("id").ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "id" FROM "test"`)
+
+ selectSQL, _, err = ds1.Select("id", "name").ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "id", "name" FROM "test"`)
+
+ selectSQL, _, err = ds1.Select(L("COUNT(*)").As("count")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT COUNT(*) AS "count" FROM "test"`)
+
+ selectSQL, _, err = ds1.Select(C("id").As("other_id"), L("COUNT(*)").As("count")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "id" AS "other_id", COUNT(*) AS "count" FROM "test"`)
+
+ selectSQL, _, err = ds1.From().Select(ds1.From("test_1").Select("id")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT (SELECT "id" FROM "test_1")`)
+
+ selectSQL, _, err = ds1.From().Select(ds1.From("test_1").Select("id").As("test_id")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT (SELECT "id" FROM "test_1") AS "test_id"`)
+
+ selectSQL, _, err = ds1.From().
+ Select(
+ DISTINCT("a").As("distinct"),
+ COUNT("a").As("count"),
+ L("CASE WHEN ? THEN ? ELSE ? END", MIN("a").Eq(10), true, false),
+ L("CASE WHEN ? THEN ? ELSE ? END", AVG("a").Neq(10), true, false),
+ L("CASE WHEN ? THEN ? ELSE ? END", FIRST("a").Gt(10), true, false),
+ L("CASE WHEN ? THEN ? ELSE ? END", FIRST("a").Gte(10), true, false),
+ L("CASE WHEN ? THEN ? ELSE ? END", LAST("a").Lt(10), true, false),
+ L("CASE WHEN ? THEN ? ELSE ? END", LAST("a").Lte(10), true, false),
+ SUM("a").As("sum"),
+ COALESCE(C("a"), "a").As("colaseced"),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT `+
+ `DISTINCT("a") AS "distinct", `+
+ `COUNT("a") AS "count", `+
+ `CASE WHEN (MIN("a") = 10) THEN TRUE ELSE FALSE END, `+
+ `CASE WHEN (AVG("a") != 10) THEN TRUE ELSE FALSE END, `+
+ `CASE WHEN (FIRST("a") > 10) THEN TRUE ELSE FALSE END, `+
+ `CASE WHEN (FIRST("a") >= 10) THEN TRUE ELSE FALSE END,`+
+ ` CASE WHEN (LAST("a") < 10) THEN TRUE ELSE FALSE END, `+
+ `CASE WHEN (LAST("a") <= 10) THEN TRUE ELSE FALSE END, `+
+ `SUM("a") AS "sum", `+
+ `COALESCE("a", 'a') AS "colaseced"`)
+
+ type MyStruct struct {
+ Name string
+ Address string `db:"address"`
+ EmailAddress string `db:"email_address"`
+ FakeCol string `db:"-"`
+ }
+ selectSQL, _, err = ds1.Select(&MyStruct{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
+
+ selectSQL, _, err = ds1.Select(MyStruct{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
+
+ type myStruct2 struct {
+ MyStruct
+ Zipcode string `db:"zipcode"`
+ }
+
+ selectSQL, _, err = ds1.Select(&myStruct2{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name", "zipcode" FROM "test"`)
+
+ selectSQL, _, err = ds1.Select(myStruct2{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name", "zipcode" FROM "test"`)
+
+ var myStructs []MyStruct
+ selectSQL, _, err = ds1.Select(&myStructs).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
+
+ selectSQL, _, err = ds1.Select(myStructs).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "address", "email_address", "name" FROM "test"`)
+ // should not change original
+ selectSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestSelectDistinct() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetSelectDistinct(exp.NewColumnListExpression(C("a")))
+ assert.Equal(t, ec, ds.SelectDistinct(C("a")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestSelectDistinct_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ selectSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct("id").ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "id" FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct("id", "name").ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "id", "name" FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct(L("COUNT(*)").As("count")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT COUNT(*) AS "count" FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct(C("id").As("other_id"), L("COUNT(*)").As("count")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "id" AS "other_id", COUNT(*) AS "count" FROM "test"`)
+
+ type MyStruct struct {
+ Name string
+ Address string `db:"address"`
+ EmailAddress string `db:"email_address"`
+ FakeCol string `db:"-"`
+ }
+ selectSQL, _, err = ds1.SelectDistinct(&MyStruct{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct(MyStruct{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
+
+ type myStruct2 struct {
+ MyStruct
+ Zipcode string `db:"zipcode"`
+ }
+
+ selectSQL, _, err = ds1.SelectDistinct(&myStruct2{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name", "zipcode" FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct(myStruct2{}).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name", "zipcode" FROM "test"`)
+
+ var myStructs []MyStruct
+ selectSQL, _, err = ds1.SelectDistinct(&myStructs).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
+
+ selectSQL, _, err = ds1.SelectDistinct(myStructs).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT DISTINCT "address", "email_address", "name" FROM "test"`)
+ // should not change original
+ selectSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+ // should not change original
+ selectSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestClearSelect() {
+ t := sds.T()
+ ds := From("test").Select(C("a"))
+ dsc := ds.GetClauses()
+ ec := dsc.SetSelect(exp.NewColumnListExpression(Star()))
+ assert.Equal(t, ec, ds.ClearSelect().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestClearSelect_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ selectSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ b := ds1.Select("a").ClearSelect()
+ selectSQL, _, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestSelectAppend(selects ...interface{}) {
+ t := sds.T()
+ ds := From("test").Select(C("a"))
+ dsc := ds.GetClauses()
+ ec := dsc.SelectAppend(exp.NewColumnListExpression(C("b")))
+ assert.Equal(t, ec, ds.SelectAppend(C("b")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestSelectAppend_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ selectSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ b := ds1.Select("a").SelectAppend("b").SelectAppend("c")
+ selectSQL, _, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT "a", "b", "c" FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestFrom() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetFrom(exp.NewColumnListExpression(T("t")))
+ assert.Equal(t, ec, ds.From(T("t")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestFrom_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ selectSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ ds2 := ds1.From("test2")
+ selectSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test2"`)
+
+ ds2 = ds1.From("test2", "test3")
+ selectSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test2", "test3"`)
+
+ ds2 = ds1.From(T("test2").As("test_2"), "test3")
+ selectSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test2" AS "test_2", "test3"`)
+
+ ds2 = ds1.From(ds1.From("test2"), "test3")
+ selectSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM (SELECT * FROM "test2") AS "t1", "test3"`)
+
+ ds2 = ds1.From(ds1.From("test2").As("test_2"), "test3")
+ selectSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM (SELECT * FROM "test2") AS "test_2", "test3"`)
+ // should not change original
+ selectSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestFromSelf() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetFrom(exp.NewColumnListExpression(ds.As("t1")))
+ assert.Equal(t, ec, ds.FromSelf().GetClauses())
+
+ ec2 := dsc.SetFrom(exp.NewColumnListExpression(ds.As("test")))
+ assert.Equal(t, ec2, ds.As("test").FromSelf().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestCompoundFromSelf() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ assert.Equal(t, dsc, ds.CompoundFromSelf().GetClauses())
+
+ ds2 := ds.Limit(1)
+ dsc2 := exp.NewSelectClauses().SetFrom(exp.NewColumnListExpression(ds2.As("t1")))
+ assert.Equal(t, dsc2, ds2.CompoundFromSelf().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.InnerJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.Join(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+
+ b := ds1.Join(T("players").As("p"), On(Ex{"p.id": I("items.playerId")}))
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "players" AS "p" ON ("p"."id" = "items"."playerId")`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "players" AS "p" ON ("p"."id" = "items"."playerId")`)
+
+ b = ds1.Join(ds1.From("players").As("p"), On(Ex{"p.id": I("items.playerId")}))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN (SELECT * FROM "players") AS "p" ON ("p"."id" = "items"."playerId")`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN (SELECT * FROM "players") AS "p" ON ("p"."id" = "items"."playerId")`)
+
+ b = ds1.Join(S("v1").Table("test"), On(Ex{"v1.test.id": I("items.playerId")}))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "v1"."test" ON ("v1"."test"."id" = "items"."playerId")`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "v1"."test" ON ("v1"."test"."id" = "items"."playerId")`)
+
+ b = ds1.Join(T("test"), Using(C("name"), C("common_id")))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
+
+ b = ds1.Join(T("test"), Using("name", "common_id"))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" INNER JOIN "test" USING ("name", "common_id")`)
+
+ b = ds1.Join(
+ T("categories"),
+ On(
+ I("categories.categoryId").Eq(I("items.id")),
+ I("categories.categoryId").In(1, 2, 3),
+ ),
+ )
+
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "categories" ON (`+
+ `("categories"."categoryId" = "items"."id") AND ("categories"."categoryId" IN (1, 2, 3))`+
+ `)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1), int64(2), int64(3)})
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "categories" ON (`+
+ `("categories"."categoryId" = "items"."id") AND ("categories"."categoryId" IN (?, ?, ?))`+
+ `)`)
+}
+
+func (sds *selectDatasetSuite) TestInnerJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.InnerJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.InnerJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestInnerJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.
+ InnerJoin(T("b"), On(Ex{"b.itemsId": I("items.id")})).
+ LeftOuterJoin(T("c"), On(Ex{"c.b_id": I("b.id")})).
+ ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "b" ON ("b"."itemsId" = "items"."id") `+
+ `LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")`)
+
+ selectSQL, _, err = ds1.
+ InnerJoin(T("b"), On(Ex{"b.itemsId": I("items.id")})).
+ LeftOuterJoin(T("c"), On(Ex{"c.b_id": I("b.id")})).
+ ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "b" ON ("b"."itemsId" = "items"."id") `+
+ `LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")`)
+
+ selectSQL, _, err = ds1.InnerJoin(
+ T("categories"),
+ On(Ex{"categories.categoryId": I("items.id")}),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `INNER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+}
+
+func (sds *selectDatasetSuite) TestFullOuterJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.FullOuterJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.FullOuterJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestFullOuterJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.
+ FullOuterJoin(T("categories"), On(Ex{"categories.categoryId": I("items.id")})).
+ Order(C("stamp").Asc()).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id") ORDER BY "stamp" ASC`)
+
+ selectSQL, _, err = ds1.FullOuterJoin(
+ T("categories"),
+ On(Ex{"categories.categoryId": I("items.id")}),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `FULL OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+}
+
+func (sds *selectDatasetSuite) TestRightOuterJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.RightOuterJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.RightOuterJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestRightOuterJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.RightOuterJoin(
+ T("categories"),
+ On(Ex{"categories.categoryId": I("items.id")}),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `RIGHT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+}
+
+func (sds *selectDatasetSuite) TestLeftOuterJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.LeftOuterJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.LeftOuterJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestLeftOuterJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+
+ selectSQL, _, err := ds1.LeftOuterJoin(T("categories"), On(Ex{
+ "categories.categoryId": I("items.id"),
+ })).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `LEFT OUTER JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+
+ selectSQL, _, err = ds1.
+ LeftOuterJoin(
+ T("categories"),
+ On(
+ I("categories.categoryId").Eq(I("items.id")),
+ I("categories.categoryId").In(1, 2, 3)),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, `SELECT * FROM "items" `+
+ `LEFT OUTER JOIN "categories" `+
+ `ON (("categories"."categoryId" = "items"."id") AND ("categories"."categoryId" IN (1, 2, 3)))`, selectSQL)
+
+}
+
+func (sds *selectDatasetSuite) TestFullJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.FullJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.FullJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestFullJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.FullJoin(
+ T("categories"),
+ On(Ex{"categories.categoryId": I("items.id")}),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `FULL JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+}
+
+func (sds *selectDatasetSuite) TestRightJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.RightJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.RightJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestRightJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.RightJoin(
+ T("categories"),
+ On(Ex{"categories.categoryId": I("items.id")}),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `RIGHT JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+}
+
+func (sds *selectDatasetSuite) TestLeftJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewConditionedJoinExpression(exp.LeftJoinType, T("foo"), On(C("a").IsNull())),
+ )
+ assert.Equal(t, ec, ds.LeftJoin(T("foo"), On(C("a").IsNull())).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestLeftJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.LeftJoin(
+ T("categories"),
+ On(Ex{"categories.categoryId": I("items.id")}),
+ ).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" `+
+ `LEFT JOIN "categories" ON ("categories"."categoryId" = "items"."id")`)
+}
+
+func (sds *selectDatasetSuite) TestNaturalJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, T("foo")),
+ )
+ assert.Equal(t, ec, ds.NaturalJoin(T("foo")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestNaturalJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.NaturalJoin(T("categories")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL JOIN "categories"`)
+}
+
+func (sds *selectDatasetSuite) TestNaturalLeftJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewUnConditionedJoinExpression(exp.NaturalLeftJoinType, T("foo")),
+ )
+ assert.Equal(t, ec, ds.NaturalLeftJoin(T("foo")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestNaturalLeftJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.NaturalLeftJoin(T("categories")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL LEFT JOIN "categories"`)
+
+}
+
+func (sds *selectDatasetSuite) TestNaturalRightJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.NaturalRightJoin(T("categories")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL RIGHT JOIN "categories"`)
+}
+
+func (sds *selectDatasetSuite) TestNaturalRightJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewUnConditionedJoinExpression(exp.NaturalRightJoinType, T("foo")),
+ )
+ assert.Equal(t, ec, ds.NaturalRightJoin(T("foo")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+func (sds *selectDatasetSuite) TestNaturalFullJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewUnConditionedJoinExpression(exp.NaturalFullJoinType, T("foo")),
+ )
+ assert.Equal(t, ec, ds.NaturalFullJoin(T("foo")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestNaturalFullJoin_ToSQL() {
+ t := sds.T()
+ ds1 := From("items")
+ selectSQL, _, err := ds1.NaturalFullJoin(T("categories")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" NATURAL FULL JOIN "categories"`)
+}
+
+func (sds *selectDatasetSuite) TestCrossJoin() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.JoinsAppend(
+ exp.NewUnConditionedJoinExpression(exp.CrossJoinType, T("foo")),
+ )
+ assert.Equal(t, ec, ds.CrossJoin(T("foo")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestCrossJoin_ToSQL() {
+ t := sds.T()
+ selectSQL, _, err := From("items").CrossJoin(T("categories")).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "items" CROSS JOIN "categories"`)
+}
+
+func (sds *selectDatasetSuite) TestWhere() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ w := Ex{
+ "a": 1,
+ }
+ ec := dsc.WhereAppend(w)
+ assert.Equal(t, ec, ds.Where(w).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestWhere_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(
+ C("a").Eq(true),
+ C("a").Neq(true),
+ C("a").Eq(false),
+ C("a").Neq(false),
+ )
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
+
+ b = ds1.Where(
+ C("a").Eq("a"),
+ C("b").Neq("b"),
+ C("c").Gt("c"),
+ C("d").Gte("d"),
+ C("e").Lt("e"),
+ C("f").Lte("f"),
+ )
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a", "b", "c", "d", "e", "f"}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("a" = ?) AND ("b" != ?) AND ("c" > ?) AND ("d" >= ?) AND ("e" < ?) AND ("f" <= ?))`)
+
+ b = ds1.Where(
+ C("a").Eq(From("test2").Select("id")),
+ )
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ b = ds1.Where(Ex{
+ "a": "a",
+ "b": Op{"neq": "b"},
+ "c": Op{"gt": "c"},
+ "d": Op{"gte": "d"},
+ "e": Op{"lt": "e"},
+ "f": Op{"lte": "f"},
+ })
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a", "b", "c", "d", "e", "f"}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("a" = ?) AND ("b" != ?) AND ("c" > ?) AND ("d" >= ?) AND ("e" < ?) AND ("f" <= ?))`)
+
+ b = ds1.Where(Ex{
+ "a": From("test2").Select("id"),
+ })
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+}
+
+func (sds *selectDatasetSuite) TestWhere_ToSQLEmpty() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where()
+ selectSQL, _, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestWhere_ToSQLWithChain() {
+ t := sds.T()
+ ds1 := From("test").Where(
+ C("x").Eq(0),
+ C("y").Eq(1),
+ )
+
+ ds2 := ds1.Where(
+ C("z").Eq(2),
+ )
+
+ a := ds2.Where(
+ C("a").Eq("A"),
+ )
+ b := ds2.Where(
+ C("b").Eq("B"),
+ )
+ selectSQL, _, err := a.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("a" = 'A'))`)
+ selectSQL, _, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" `+
+ `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("b" = 'B'))`)
+}
+
+func (sds *selectDatasetSuite) TestClearWhere() {
+ t := sds.T()
+ w := Ex{
+ "a": 1,
+ }
+ ds := From("test").Where(w)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearWhere()
+ assert.Equal(t, ec, ds.ClearWhere().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestClearWhere_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(
+ C("a").Eq(1),
+ ).ClearWhere()
+ selectSQL, _, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestForUpdate() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLock(exp.NewLock(exp.ForUpdate, NoWait))
+ assert.Equal(t, ec, ds.ForUpdate(NoWait).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestForUpdate_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).ForUpdate(Wait)
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR UPDATE `)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR UPDATE `)
+
+ b = ds1.Where(C("a").Gt(1)).ForUpdate(NoWait)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR UPDATE NOWAIT`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR UPDATE NOWAIT`)
+
+ b = ds1.Where(C("a").Gt(1)).ForUpdate(SkipLocked)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR UPDATE SKIP LOCKED`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR UPDATE SKIP LOCKED`)
+}
+
+func (sds *selectDatasetSuite) TestForNoKeyUpdate() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLock(exp.NewLock(exp.ForNoKeyUpdate, NoWait))
+ assert.Equal(t, ec, ds.ForNoKeyUpdate(NoWait).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestForNoKeyUpdate_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).ForNoKeyUpdate(Wait)
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR NO KEY UPDATE `)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR NO KEY UPDATE `)
+
+ b = ds1.Where(C("a").Gt(1)).ForNoKeyUpdate(NoWait)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR NO KEY UPDATE NOWAIT`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR NO KEY UPDATE NOWAIT`)
+
+ b = ds1.Where(C("a").Gt(1)).ForNoKeyUpdate(SkipLocked)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR NO KEY UPDATE SKIP LOCKED`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR NO KEY UPDATE SKIP LOCKED`)
+}
+
+func (sds *selectDatasetSuite) TestForKeyShare() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLock(exp.NewLock(exp.ForKeyShare, NoWait))
+ assert.Equal(t, ec, ds.ForKeyShare(NoWait).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestForKeyShare_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).ForKeyShare(Wait)
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR KEY SHARE `)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR KEY SHARE `)
+
+ b = ds1.Where(C("a").Gt(1)).ForKeyShare(NoWait)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR KEY SHARE NOWAIT`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR KEY SHARE NOWAIT`)
+
+ b = ds1.Where(C("a").Gt(1)).ForKeyShare(SkipLocked)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR KEY SHARE SKIP LOCKED`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR KEY SHARE SKIP LOCKED`)
+}
+
+func (sds *selectDatasetSuite) TestForShare() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLock(exp.NewLock(exp.ForShare, NoWait))
+ assert.Equal(t, ec, ds.ForShare(NoWait).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestForShare_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).ForShare(Wait)
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR SHARE `)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR SHARE `)
+
+ b = ds1.Where(C("a").Gt(1)).ForShare(NoWait)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR SHARE NOWAIT`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR SHARE NOWAIT`)
+
+ b = ds1.Where(C("a").Gt(1)).ForShare(SkipLocked)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) FOR SHARE SKIP LOCKED`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) FOR SHARE SKIP LOCKED`)
+}
+
+func (sds *selectDatasetSuite) TestGroupBy() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetGroupBy(exp.NewColumnListExpression(C("a")))
+ assert.Equal(t, ec, ds.GroupBy("a").GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestGroupBy_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).GroupBy("created")
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) GROUP BY "created"`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) GROUP BY "created"`)
+
+ b = ds1.Where(C("a").Gt(1)).GroupBy(L("created::DATE"))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) GROUP BY created::DATE`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) GROUP BY created::DATE`)
+
+ b = ds1.Where(C("a").Gt(1)).GroupBy("name", L("created::DATE"))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) GROUP BY "name", created::DATE`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) GROUP BY "name", created::DATE`)
+}
+
+func (sds *selectDatasetSuite) TestHaving() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ h := C("a").Gt(1)
+ ec := dsc.HavingAppend(h)
+ assert.Equal(t, ec, ds.Having(h).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestHaving_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Having(Ex{"a": Op{"gt": 1}}).GroupBy("created")
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" GROUP BY "created" HAVING ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" GROUP BY "created" HAVING ("a" > ?)`)
+
+ b = ds1.Where(Ex{"b": true}).Having(Ex{"a": Op{"gt": 1}}).GroupBy("created")
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("b" IS TRUE) GROUP BY "created" HAVING ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("b" IS TRUE) GROUP BY "created" HAVING ("a" > ?)`)
+
+ b = ds1.Having(Ex{"a": Op{"gt": 1}})
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING ("a" > ?)`)
+
+ b = ds1.Having(Ex{"a": Op{"gt": 1}}).Having(Ex{"b": 2})
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING (("a" > 1) AND ("b" = 2))`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1), int64(2)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" HAVING (("a" > ?) AND ("b" = ?))`)
+
+ b = ds1.GroupBy("name").Having(SUM("amount").Gt(0))
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" GROUP BY "name" HAVING (SUM("amount") > 0)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(0)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" GROUP BY "name" HAVING (SUM("amount") > ?)`)
+}
+
+func (sds *selectDatasetSuite) TestOrder() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ o := C("a").Desc()
+ ec := dsc.SetOrder(o)
+ assert.Equal(t, ec, ds.Order(o).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestOrder_ToSQL() {
+ t := sds.T()
+
+ ds1 := From("test")
+
+ b := ds1.Order(C("a").Asc(), L(`("a" + "b" > 2)`).Asc())
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
+}
+
+func (sds *selectDatasetSuite) TestOrderAppend() {
+ t := sds.T()
+ ds := From("test").Order(C("a").Desc())
+ dsc := ds.GetClauses()
+ o := C("b").Desc()
+ ec := dsc.OrderAppend(o)
+ assert.Equal(t, ec, ds.OrderAppend(o).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestOrderAppend_ToSQL() {
+ t := sds.T()
+ b := From("test").Order(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ b = From("test").OrderAppend(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+}
+
+func (sds *selectDatasetSuite) TestClearOrder() {
+ t := sds.T()
+ ds := From("test").Order(C("a").Desc())
+ dsc := ds.GetClauses()
+ ec := dsc.ClearOrder()
+ assert.Equal(t, ec, ds.ClearOrder().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestClearOrder_ToSQL() {
+ t := sds.T()
+ b := From("test").Order(C("a").Asc().NullsFirst()).ClearOrder()
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test"`)
+}
+
+func (sds *selectDatasetSuite) TestLimit() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLimit(uint(1))
+ assert.Equal(t, ec, ds.Limit(1).GetClauses())
+ assert.Equal(t, dsc, ds.Limit(0).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestLimit_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).Limit(10)
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) LIMIT 10`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1), int64(10)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) LIMIT ?`)
+
+ b = ds1.Where(C("a").Gt(1)).Limit(0)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
+}
+
+func (sds *selectDatasetSuite) TestLimitAll() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLimit(L("ALL"))
+ assert.Equal(t, ec, ds.LimitAll().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestLimitAll_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).LimitAll()
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) LIMIT ALL`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) LIMIT ALL`)
+
+ b = ds1.Where(C("a").Gt(1)).Limit(0).LimitAll()
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) LIMIT ALL`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) LIMIT ALL`)
+}
+
+func (sds *selectDatasetSuite) TestClearLimit() {
+ t := sds.T()
+ ds := From("test").Limit(1)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearLimit()
+ assert.Equal(t, ec, ds.ClearLimit().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestClearLimit_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).LimitAll().ClearLimit()
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
+
+ b = ds1.Where(C("a").Gt(1)).Limit(10).ClearLimit()
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
+}
+
+func (sds *selectDatasetSuite) TestOffset() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetOffset(1)
+ assert.Equal(t, ec, ds.Offset(1).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestOffset_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).Offset(10)
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1) OFFSET 10`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1), int64(10)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?) OFFSET ?`)
+
+ b = ds1.Where(C("a").Gt(1)).Offset(0)
+ selectSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
+}
+
+func (sds *selectDatasetSuite) TestClearOffset() {
+ t := sds.T()
+ ds := From("test").Offset(1)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearOffset()
+ assert.Equal(t, ec, ds.ClearOffset().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestClearOffset_ToSQL() {
+ t := sds.T()
+ ds1 := From("test")
+
+ b := ds1.Where(C("a").Gt(1)).Offset(10).ClearOffset()
+ selectSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > 1)`)
+
+ selectSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{int64(1)}, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM "test" WHERE ("a" > ?)`)
+}
+
+func (sds *selectDatasetSuite) TestUnion() {
+ t := sds.T()
+ uds := From("union_test")
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.UnionCompoundType, uds))
+ assert.Equal(t, ec, ds.Union(uds).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestUnion_ToSQL() {
+ t := sds.T()
+ a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
+ b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
+
+ ds := a.Union(b)
+ selectSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Limit(1).Union(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Order(C("id").Asc()).Union(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) ORDER BY "id" ASC) AS "t1" `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
+
+ ds = a.Union(b.Limit(1))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
+
+ ds = a.Union(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.Limit(1).Union(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
+ `UNION (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.Union(b).Union(b.Where(C("id").Lt(50)))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)) `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < 10) AND ("id" < 50)))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(10), int64(50)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)) `+
+ `UNION (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < ?) AND ("id" < ?)))`)
+
+}
+
+func (sds *selectDatasetSuite) TestUnionAll() {
+ t := sds.T()
+ uds := From("union_test")
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.UnionAllCompoundType, uds))
+ assert.Equal(t, ec, ds.UnionAll(uds).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestUnionAll_ToSQL() {
+ t := sds.T()
+ a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
+ b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
+
+ ds := a.UnionAll(b)
+ selectSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Limit(1).UnionAll(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Order(C("id").Asc()).UnionAll(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) ORDER BY "id" ASC) AS "t1" `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
+
+ ds = a.UnionAll(b.Limit(1))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
+
+ ds = a.UnionAll(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.Limit(1).UnionAll(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
+ `UNION ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.UnionAll(b).UnionAll(b.Where(C("id").Lt(50)))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)) `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < 10) AND ("id" < 50)))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(10), int64(50)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)) `+
+ `UNION ALL (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < ?) AND ("id" < ?)))`)
+
+}
+
+func (sds *selectDatasetSuite) TestIntersect() {
+ t := sds.T()
+ uds := From("union_test")
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.IntersectCompoundType, uds))
+ assert.Equal(t, ec, ds.Intersect(uds).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestIntersect_ToSQL() {
+ t := sds.T()
+ a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
+ b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
+
+ ds := a.Intersect(b)
+ selectSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Limit(1).Intersect(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Order(C("id").Asc()).Intersect(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) ORDER BY "id" ASC) AS "t1" `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
+
+ ds = a.Intersect(b.Limit(1))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
+
+ ds = a.Intersect(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.Limit(1).Intersect(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
+ `INTERSECT (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.Intersect(b).Intersect(b.Where(C("id").Lt(50)))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)) `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < 10) AND ("id" < 50)))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(10), int64(50)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)) `+
+ `INTERSECT (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < ?) AND ("id" < ?)))`)
+}
+
+func (sds *selectDatasetSuite) TestIntersectAll() {
+ t := sds.T()
+ uds := From("union_test")
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CompoundsAppend(exp.NewCompoundExpression(exp.IntersectAllCompoundType, uds))
+ assert.Equal(t, ec, ds.IntersectAll(uds).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestIntersectAll_ToSQL() {
+ t := sds.T()
+ a := From("invoice").Select("id", "amount").Where(C("amount").Gt(1000))
+ b := From("invoice").Select("id", "amount").Where(C("amount").Lt(10))
+
+ ds := a.IntersectAll(b)
+ selectSQL, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Limit(1).IntersectAll(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ ds = a.Order(C("id").Asc()).IntersectAll(b)
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) ORDER BY "id" ASC) AS "t1" `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM `+
+ `(SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) ORDER BY "id" ASC) AS "t1" `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?))`)
+
+ ds = a.IntersectAll(b.Limit(1))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) LIMIT 1) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(1)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) LIMIT ?) AS "t1")`)
+
+ ds = a.IntersectAll(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.Limit(1).IntersectAll(b.Order(C("id").Desc()))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) LIMIT 1) AS "t1" `+
+ `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10) ORDER BY "id" DESC) AS "t1")`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(1), int64(10)})
+ assert.Equal(t, selectSQL, `SELECT * FROM (`+
+ `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) LIMIT ?) AS "t1" `+
+ `INTERSECT ALL (SELECT * FROM (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?) ORDER BY "id" DESC) AS "t1")`)
+
+ ds = a.IntersectAll(b).IntersectAll(b.Where(C("id").Lt(50)))
+ selectSQL, args, err = ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > 1000) `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < 10)) `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < 10) AND ("id" < 50)))`)
+
+ selectSQL, args, err = ds.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{int64(1000), int64(10), int64(10), int64(50)})
+ assert.Equal(t, selectSQL, `SELECT "id", "amount" FROM "invoice" WHERE ("amount" > ?) `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE ("amount" < ?)) `+
+ `INTERSECT ALL (SELECT "id", "amount" FROM "invoice" WHERE (("amount" < ?) AND ("id" < ?)))`)
+}
+
+func (sds *selectDatasetSuite) TestAs() {
+ t := sds.T()
+ ds := From("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetAlias(T("a"))
+ assert.Equal(t, ec, ds.As("a").GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (sds *selectDatasetSuite) TestToSQL() {
+ t := sds.T()
+ md := new(mocks.SQLDialect)
+ ds := From("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToSelectSQL", sqlB, c).Return(nil).Once()
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (sds *selectDatasetSuite) TestToSQL_ReturnedError() {
+ t := sds.T()
+ md := new(mocks.SQLDialect)
+ ds := From("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ ee := errors.New("expected error")
+ md.On("ToSelectSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(ee)
+ }).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Equal(t, ee, err)
+ md.AssertExpectations(t)
+}
+
+func (sds *selectDatasetSuite) TestAppendSQL() {
+ t := sds.T()
+ md := new(mocks.SQLDialect)
+ ds := From("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToSelectSQL", sqlB, c).Return(nil).Once()
+ ds.AppendSQL(sqlB)
+ assert.NoError(t, sqlB.Error())
+ md.AssertExpectations(t)
+}
+
+func (sds *selectDatasetSuite) TestScanStructs() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(`SELECT "address", "name" FROM "items"`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
+ FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
+
+ sqlMock.ExpectQuery(`SELECT DISTINCT "name" FROM "items"`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
+ FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
+
+ sqlMock.ExpectQuery(`SELECT "test" FROM "items"`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var items []dsTestActionItem
+ assert.NoError(t, ds.From("items").ScanStructs(&items))
+ assert.Equal(t, items, []dsTestActionItem{
+ {Address: "111 Test Addr", Name: "Test1"},
+ {Address: "211 Test Addr", Name: "Test2"},
+ })
+
+ items = items[0:0]
+ assert.NoError(t, ds.From("items").SelectDistinct("name").ScanStructs(&items))
+ assert.Equal(t, items, []dsTestActionItem{
+ {Address: "111 Test Addr", Name: "Test1"},
+ {Address: "211 Test Addr", Name: "Test2"},
+ })
+
+ items = items[0:0]
+ assert.EqualError(t, ds.From("items").ScanStructs(items),
+ "goqu: type must be a pointer to a slice when scanning into structs")
+ assert.EqualError(t, ds.From("items").ScanStructs(&dsTestActionItem{}),
+ "goqu: type must be a pointer to a slice when scanning into structs")
+ assert.EqualError(t, ds.From("items").Select("test").ScanStructs(&items),
+ `goqu: unable to find corresponding field to column "test" returned by query`)
+}
+
+func (sds *selectDatasetSuite) TestScanStructs_WithPreparedStatements() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(
+ `SELECT "address", "name" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
+ WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).
+ FromCSVString("111 Test Addr,Test1\n211 Test Addr,Test2"))
+
+ sqlMock.ExpectQuery(
+ `SELECT "test" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
+ WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var items []dsTestActionItem
+ assert.NoError(t, ds.From("items").Prepared(true).Where(Ex{
+ "name": []string{"Bob", "Sally", "Billy"},
+ "address": "111 Test Addr",
+ }).ScanStructs(&items))
+ assert.Equal(t, items, []dsTestActionItem{
+ {Address: "111 Test Addr", Name: "Test1"},
+ {Address: "211 Test Addr", Name: "Test2"},
+ })
+
+ items = items[0:0]
+ assert.EqualError(t, ds.From("items").ScanStructs(items),
+ "goqu: type must be a pointer to a slice when scanning into structs")
+ assert.EqualError(t, ds.From("items").ScanStructs(&dsTestActionItem{}),
+ "goqu: type must be a pointer to a slice when scanning into structs")
+ assert.EqualError(t, ds.From("items").
+ Prepared(true).
+ Select("test").
+ Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
+ ScanStructs(&items), `goqu: unable to find corresponding field to column "test" returned by query`)
+}
+
+func (sds *selectDatasetSuite) TestScanStruct() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(`SELECT "address", "name" FROM "items" LIMIT 1`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
+
+ sqlMock.ExpectQuery(`SELECT DISTINCT "name" FROM "items" LIMIT 1`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
+
+ sqlMock.ExpectQuery(`SELECT "test" FROM "items" LIMIT 1`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var item dsTestActionItem
+ found, err := ds.From("items").ScanStruct(&item)
+ assert.NoError(t, err)
+ assert.True(t, found)
+ assert.Equal(t, item.Address, "111 Test Addr")
+ assert.Equal(t, item.Name, "Test1")
+
+ item = dsTestActionItem{}
+ found, err = ds.From("items").SelectDistinct("name").ScanStruct(&item)
+ assert.NoError(t, err)
+ assert.True(t, found)
+ assert.Equal(t, item.Address, "111 Test Addr")
+ assert.Equal(t, item.Name, "Test1")
+
+ _, err = ds.From("items").ScanStruct(item)
+ assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
+ _, err = ds.From("items").ScanStruct([]dsTestActionItem{})
+ assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
+ _, err = ds.From("items").Select("test").ScanStruct(&item)
+ assert.EqualError(t, err, `goqu: unable to find corresponding field to column "test" returned by query`)
+}
+
+func (sds *selectDatasetSuite) TestScanStruct_WithPreparedStatements() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(
+ `SELECT "address", "name" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\) LIMIT \?`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
+ WillReturnRows(sqlmock.NewRows([]string{"address", "name"}).FromCSVString("111 Test Addr,Test1"))
+
+ sqlMock.ExpectQuery(`SELECT "test" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\) LIMIT \?`).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
+ WillReturnRows(sqlmock.NewRows([]string{"test"}).FromCSVString("test1\ntest2"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var item dsTestActionItem
+ found, err := ds.From("items").Prepared(true).Where(Ex{
+ "name": []string{"Bob", "Sally", "Billy"},
+ "address": "111 Test Addr",
+ }).ScanStruct(&item)
+ assert.NoError(t, err)
+ assert.True(t, found)
+ assert.Equal(t, item.Address, "111 Test Addr")
+ assert.Equal(t, item.Name, "Test1")
+
+ _, err = ds.From("items").ScanStruct(item)
+ assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
+ _, err = ds.From("items").ScanStruct([]dsTestActionItem{})
+ assert.EqualError(t, err, "goqu: type must be a pointer to a struct when scanning into a struct")
+ _, err = ds.From("items").
+ Prepared(true).
+ Select("test").
+ Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
+ ScanStruct(&item)
+ assert.EqualError(t, err, `goqu: unable to find corresponding field to column "test" returned by query`)
+}
+
+func (sds *selectDatasetSuite) TestScanVals() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(`SELECT "id" FROM "items"`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var ids []uint32
+ assert.NoError(t, ds.From("items").Select("id").ScanVals(&ids))
+ assert.Equal(t, ids, []uint32{1, 2, 3, 4, 5})
+
+ assert.EqualError(t, ds.From("items").ScanVals([]uint32{}),
+ "goqu: type must be a pointer to a slice when scanning into vals")
+ assert.EqualError(t, ds.From("items").ScanVals(dsTestActionItem{}),
+ "goqu: type must be a pointer to a slice when scanning into vals")
+}
+
+func (sds *selectDatasetSuite) TestScanVals_WithPreparedStatment() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(
+ `SELECT "id" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
+ WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n5"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var ids []uint32
+ assert.NoError(t, ds.From("items").
+ Prepared(true).
+ Select("id").
+ Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
+ ScanVals(&ids))
+ assert.Equal(t, ids, []uint32{1, 2, 3, 4, 5})
+
+ assert.EqualError(t, ds.From("items").ScanVals([]uint32{}),
+ "goqu: type must be a pointer to a slice when scanning into vals")
+ assert.EqualError(t, ds.From("items").ScanVals(dsTestActionItem{}),
+ "goqu: type must be a pointer to a slice when scanning into vals")
+}
+
+func (sds *selectDatasetSuite) TestScanVal() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(`SELECT "id" FROM "items" LIMIT 1`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var id int64
+ found, err := ds.From("items").Select("id").ScanVal(&id)
+ assert.NoError(t, err)
+ assert.Equal(t, id, int64(10))
+ assert.True(t, found)
+
+ found, err = ds.From("items").ScanVal([]int64{})
+ assert.False(t, found)
+ assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
+ found, err = ds.From("items").ScanVal(10)
+ assert.False(t, found)
+ assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
+}
+
+func (sds *selectDatasetSuite) TestScanVal_WithPreparedStatement() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(
+ `SELECT "id" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\) LIMIT ?`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
+ WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("10"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var id int64
+ found, err := ds.From("items").
+ Prepared(true).
+ Select("id").
+ Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
+ ScanVal(&id)
+ assert.NoError(t, err)
+ assert.Equal(t, id, int64(10))
+ assert.True(t, found)
+
+ found, err = ds.From("items").ScanVal([]int64{})
+ assert.False(t, found)
+ assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
+ found, err = ds.From("items").ScanVal(10)
+ assert.False(t, found)
+ assert.EqualError(t, err, "goqu: type must be a pointer when scanning into val")
+}
+
+func (sds *selectDatasetSuite) TestCount() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(`SELECT COUNT\(\*\) AS "count" FROM "items"`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"count"}).FromCSVString("10"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ count, err := ds.From("items").Count()
+ assert.NoError(t, err)
+ assert.Equal(t, count, int64(10))
+}
+
+func (sds *selectDatasetSuite) TestCount_WithPreparedStatement() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(
+ `SELECT COUNT\(\*\) AS "count" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy", 1).
+ WillReturnRows(sqlmock.NewRows([]string{"count"}).FromCSVString("10"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ count, err := ds.From("items").
+ Prepared(true).
+ Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
+ Count()
+ assert.NoError(t, err)
+ assert.Equal(t, count, int64(10))
+}
+
+func (sds *selectDatasetSuite) TestPluck() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(`SELECT "name" FROM "items"`).
+ WithArgs().
+ WillReturnRows(sqlmock.NewRows([]string{"name"}).FromCSVString("test1\ntest2\ntest3\ntest4\ntest5"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var names []string
+ assert.NoError(t, ds.From("items").Pluck(&names, "name"))
+ assert.Equal(t, names, []string{"test1", "test2", "test3", "test4", "test5"})
+}
+
+func (sds *selectDatasetSuite) TestPluck_WithPreparedStatement() {
+ t := sds.T()
+ mDb, sqlMock, err := sqlmock.New()
+ assert.NoError(t, err)
+ sqlMock.ExpectQuery(
+ `SELECT "name" FROM "items" WHERE \(\("address" = \?\) AND \("name" IN \(\?, \?, \?\)\)\)`,
+ ).
+ WithArgs("111 Test Addr", "Bob", "Sally", "Billy").
+ WillReturnRows(sqlmock.NewRows([]string{"name"}).FromCSVString("Bob\nSally\nBilly"))
+
+ qf := exec.NewQueryFactory(mDb)
+ ds := newDataset("mock", qf)
+ var names []string
+ assert.NoError(t, ds.From("items").
+ Prepared(true).
+ Where(Ex{"name": []string{"Bob", "Sally", "Billy"}, "address": "111 Test Addr"}).
+ Pluck(&names, "name"))
+ assert.Equal(t, names, []string{"Bob", "Sally", "Billy"})
+}
+
+func TestSelectDataset(t *testing.T) {
+ suite.Run(t, new(selectDatasetSuite))
+}
diff --git a/sql_dialect.go b/sql_dialect.go
index fc309106..93ab9475 100644
--- a/sql_dialect.go
+++ b/sql_dialect.go
@@ -8,26 +8,28 @@ import (
"time"
"unicode/utf8"
- "github.com/doug-martin/goqu/v7/exp"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/sb"
- "github.com/doug-martin/goqu/v7/internal/util"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+ "github.com/doug-martin/goqu/v8/internal/util"
)
type (
// An adapter interface to be used by a Dataset to generate SQL for a specific dialect.
// See DefaultAdapter for a concrete implementation and examples.
SQLDialect interface {
- ToSelectSQL(b sb.SQLBuilder, clauses exp.Clauses)
- ToUpdateSQL(b sb.SQLBuilder, clauses exp.Clauses, update interface{})
- ToInsertSQL(b sb.SQLBuilder, clauses exp.Clauses, ie exp.InsertExpression)
- ToDeleteSQL(b sb.SQLBuilder, clauses exp.Clauses)
- ToTruncateSQL(b sb.SQLBuilder, clauses exp.Clauses, options exp.TruncateOptions)
+ Dialect() string
+ ToSelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses)
+ ToUpdateSQL(b sb.SQLBuilder, clauses exp.UpdateClauses)
+ ToInsertSQL(b sb.SQLBuilder, clauses exp.InsertClauses)
+ ToDeleteSQL(b sb.SQLBuilder, clauses exp.DeleteClauses)
+ ToTruncateSQL(b sb.SQLBuilder, clauses exp.TruncateClauses)
}
// The default adapter. This class should be used when building a new adapter. When creating a new adapter you can
// either override methods, or more typically update default values.
// See (github.com/doug-martin/goqu/adapters/postgres)
sqlDialect struct {
+ dialect string
dialectOptions *SQLDialectOptions
}
)
@@ -87,7 +89,8 @@ func init() {
}
func RegisterDialect(name string, do *SQLDialectOptions) {
- dialects[strings.ToLower(name)] = newDialect(do)
+ lowerName := strings.ToLower(name)
+ dialects[lowerName] = newDialect(lowerName, do)
}
func DeregisterDialect(name string) {
@@ -99,13 +102,16 @@ func GetDialect(name string) SQLDialect {
if d, ok := dialects[name]; ok {
return d
}
- return newDialect(DefaultDialectOptions())
+ return newDialect("default", DefaultDialectOptions())
}
-func newDialect(do *SQLDialectOptions) SQLDialect {
- return &sqlDialect{dialectOptions: do}
+func newDialect(dialect string, do *SQLDialectOptions) SQLDialect {
+ return &sqlDialect{dialect: dialect, dialectOptions: do}
}
+func (d *sqlDialect) Dialect() string {
+ return d.dialect
+}
func (d *sqlDialect) SupportsReturn() bool {
return d.dialectOptions.SupportsReturn
}
@@ -125,7 +131,7 @@ func (d *sqlDialect) SupportsLimitOnDelete() bool {
return d.dialectOptions.SupportsLimitOnDelete
}
-func (d *sqlDialect) ToSelectSQL(b sb.SQLBuilder, clauses exp.Clauses) {
+func (d *sqlDialect) ToSelectSQL(b sb.SQLBuilder, clauses exp.SelectClauses) {
for _, f := range d.dialectOptions.SelectSQLOrder {
if b.Error() != nil {
return
@@ -165,13 +171,13 @@ func (d *sqlDialect) ToSelectSQL(b sb.SQLBuilder, clauses exp.Clauses) {
}
}
-func (d *sqlDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.Clauses, update interface{}) {
- updates, err := exp.NewUpdateExpressions(update)
+func (d *sqlDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.UpdateClauses) {
+ updates, err := exp.NewUpdateExpressions(clauses.SetValues())
if err != nil {
b.SetError(err)
return
}
- if !clauses.HasSources() {
+ if !clauses.HasTable() {
b.SetError(errNoSourceForUpdate)
return
}
@@ -185,7 +191,8 @@ func (d *sqlDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.Clauses, update in
case UpdateBeginSQLFragment:
d.UpdateBeginSQL(b)
case SourcesSQLFragment:
- d.SourcesSQL(b, clauses.From())
+ b.WriteRunes(d.dialectOptions.SpaceRune)
+ d.Literal(b, clauses.Table())
case UpdateSQLFragment:
d.UpdateExpressionsSQL(b, updates...)
case WhereSQLFragment:
@@ -208,10 +215,9 @@ func (d *sqlDialect) ToUpdateSQL(b sb.SQLBuilder, clauses exp.Clauses, update in
func (d *sqlDialect) ToInsertSQL(
b sb.SQLBuilder,
- clauses exp.Clauses,
- ie exp.InsertExpression,
+ clauses exp.InsertClauses,
) {
- if !clauses.HasSources() {
+ if !clauses.HasInto() {
b.SetError(errNoSourceForInsert)
return
}
@@ -223,11 +229,12 @@ func (d *sqlDialect) ToInsertSQL(
case CommonTableSQLFragment:
d.CommonTablesSQL(b, clauses.CommonTables())
case InsertBeingSQLFragment:
- d.InsertBeginSQL(b, ie.OnConflict())
- case SourcesSQLFragment:
- d.SourcesSQL(b, clauses.From())
+ d.InsertBeginSQL(b, clauses.OnConflict())
+ case IntoSQLFragment:
+ b.WriteRunes(d.dialectOptions.SpaceRune)
+ d.Literal(b, clauses.Into())
case InsertSQLFragment:
- d.InsertSQL(b, ie)
+ d.InsertSQL(b, clauses)
case ReturningSQLFragment:
d.ReturningSQL(b, clauses.Returning())
default:
@@ -237,8 +244,8 @@ func (d *sqlDialect) ToInsertSQL(
}
-func (d *sqlDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.Clauses) {
- if !clauses.HasSources() {
+func (d *sqlDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.DeleteClauses) {
+ if !clauses.HasFrom() {
b.SetError(errNoSourceForDelete)
return
}
@@ -252,7 +259,7 @@ func (d *sqlDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.Clauses) {
case DeleteBeginSQLFragment:
d.DeleteBeginSQL(b)
case FromSQLFragment:
- d.FromSQL(b, clauses.From())
+ d.FromSQL(b, exp.NewColumnListExpression(clauses.From()))
case WhereSQLFragment:
d.WhereSQL(b, clauses.Where())
case OrderSQLFragment:
@@ -271,8 +278,8 @@ func (d *sqlDialect) ToDeleteSQL(b sb.SQLBuilder, clauses exp.Clauses) {
}
}
-func (d *sqlDialect) ToTruncateSQL(b sb.SQLBuilder, clauses exp.Clauses, opts exp.TruncateOptions) {
- if !clauses.HasSources() {
+func (d *sqlDialect) ToTruncateSQL(b sb.SQLBuilder, clauses exp.TruncateClauses) {
+ if !clauses.HasTable() {
b.SetError(errNoSourceForTruncate)
return
}
@@ -282,7 +289,7 @@ func (d *sqlDialect) ToTruncateSQL(b sb.SQLBuilder, clauses exp.Clauses, opts ex
}
switch f {
case TruncateSQLFragment:
- d.TruncateSQL(b, clauses.From(), opts)
+ d.TruncateSQL(b, clauses.Table(), clauses.Options())
default:
b.SetError(notSupportedFragmentErr("TRUNCATE", f))
}
@@ -328,7 +335,35 @@ func (d *sqlDialect) TruncateSQL(b sb.SQLBuilder, from exp.ColumnListExpression,
}
// Adds the columns list to an insert statement
-func (d *sqlDialect) InsertSQL(b sb.SQLBuilder, ie exp.InsertExpression) {
+func (d *sqlDialect) InsertSQL(b sb.SQLBuilder, ic exp.InsertClauses) {
+ if b.Error() != nil {
+ return
+ }
+ switch {
+ case ic.HasRows():
+ ie, err := exp.NewInsertExpression(ic.Rows()...)
+ if err != nil {
+ b.SetError(err)
+ return
+ }
+ d.InsertExpressionSQL(b, ie)
+ case ic.HasCols() && ic.HasVals():
+ d.insertColumnsSQL(b, ic.Cols())
+ d.insertValuesSQL(b, ic.Vals())
+ case ic.HasCols() && ic.HasFrom():
+ d.insertColumnsSQL(b, ic.Cols())
+ d.insertFromSQL(b, ic.From())
+ case ic.HasFrom():
+ d.insertFromSQL(b, ic.From())
+ default:
+ d.defaultValuesSQL(b)
+
+ }
+
+ d.onConflictSQL(b, ic.OnConflict())
+}
+
+func (d *sqlDialect) InsertExpressionSQL(b sb.SQLBuilder, ie exp.InsertExpression) {
switch {
case b.Error() != nil:
return
@@ -340,7 +375,6 @@ func (d *sqlDialect) InsertSQL(b sb.SQLBuilder, ie exp.InsertExpression) {
d.insertColumnsSQL(b, ie.Cols())
d.insertValuesSQL(b, ie.Vals())
}
- d.onConflictSQL(b, ie.OnConflict())
}
// Adds column setters in an update SET clause
diff --git a/sql_dialect_example_test.go b/sql_dialect_example_test.go
index 6f95b029..7ffb163b 100644
--- a/sql_dialect_example_test.go
+++ b/sql_dialect_example_test.go
@@ -3,7 +3,7 @@ package goqu_test
import (
"fmt"
- "github.com/doug-martin/goqu/v7"
+ "github.com/doug-martin/goqu/v8"
)
func ExampleRegisterDialect() {
diff --git a/sql_dialect_options.go b/sql_dialect_options.go
index 9e2ad3bb..f62dfe5c 100644
--- a/sql_dialect_options.go
+++ b/sql_dialect_options.go
@@ -4,7 +4,7 @@ import (
"fmt"
"time"
- "github.com/doug-martin/goqu/v7/exp"
+ "github.com/doug-martin/goqu/v8/exp"
)
type (
@@ -289,6 +289,7 @@ const (
ForSQLFragment
UpdateBeginSQLFragment
SourcesSQLFragment
+ IntoSQLFragment
UpdateSQLFragment
ReturningSQLFragment
InsertBeingSQLFragment
@@ -297,6 +298,7 @@ const (
TruncateSQLFragment
)
+// nolint:gocyclo
func (sf SQLFragmentType) String() string {
switch sf {
case CommonTableSQLFragment:
@@ -327,6 +329,8 @@ func (sf SQLFragmentType) String() string {
return "UpdateBeginSQLFragment"
case SourcesSQLFragment:
return "SourcesSQLFragment"
+ case IntoSQLFragment:
+ return "IntoSQLFragment"
case UpdateSQLFragment:
return "UpdateSQLFragment"
case ReturningSQLFragment:
@@ -490,7 +494,7 @@ func DefaultDialectOptions() *SQLDialectOptions {
InsertSQLOrder: []SQLFragmentType{
CommonTableSQLFragment,
InsertBeingSQLFragment,
- SourcesSQLFragment,
+ IntoSQLFragment,
InsertSQLFragment,
ReturningSQLFragment,
},
diff --git a/sql_dialect_test.go b/sql_dialect_test.go
index 63a37a8a..346a4d8d 100644
--- a/sql_dialect_test.go
+++ b/sql_dialect_test.go
@@ -7,12 +7,11 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/require"
-
- "github.com/doug-martin/goqu/v7/exp"
- "github.com/doug-martin/goqu/v7/internal/errors"
- "github.com/doug-martin/goqu/v7/internal/sb"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)
@@ -23,12 +22,12 @@ type testAppendableExpression struct {
sql string
args []interface{}
err error
- clauses exp.Clauses
+ clauses exp.SelectClauses
}
-func newTestAppendableExpression(sql string, args []interface{}, err error, clauses exp.Clauses) exp.AppendableExpression {
+func newTestAppendableExpression(sql string, args []interface{}, err error, clauses exp.SelectClauses) exp.AppendableExpression {
if clauses == nil {
- clauses = exp.NewClauses()
+ clauses = exp.NewSelectClauses()
}
return &testAppendableExpression{sql: sql, args: args, err: err, clauses: clauses}
}
@@ -37,7 +36,7 @@ func (tae *testAppendableExpression) Expression() exp.Expression {
return tae
}
-func (tae *testAppendableExpression) GetClauses() exp.Clauses {
+func (tae *testAppendableExpression) GetClauses() exp.SelectClauses {
return tae.clauses
}
@@ -90,11 +89,11 @@ func (dts *dialectTestSuite) TestSupportsReturn() {
t := dts.T()
opts := DefaultDialectOptions()
opts.SupportsReturn = true
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.SupportsReturn = false
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
assert.True(t, d.SupportsReturn())
assert.False(t, d2.SupportsReturn())
@@ -104,11 +103,11 @@ func (dts *dialectTestSuite) TestSupportsOrderByOnUpdate() {
t := dts.T()
opts := DefaultDialectOptions()
opts.SupportsOrderByOnUpdate = true
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.SupportsOrderByOnUpdate = false
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
assert.True(t, d.SupportsOrderByOnUpdate())
assert.False(t, d2.SupportsOrderByOnUpdate())
@@ -118,11 +117,11 @@ func (dts *dialectTestSuite) TestSupportsLimitOnUpdate() {
t := dts.T()
opts := DefaultDialectOptions()
opts.SupportsLimitOnUpdate = true
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.SupportsLimitOnUpdate = false
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
assert.True(t, d.SupportsLimitOnUpdate())
assert.False(t, d2.SupportsLimitOnUpdate())
@@ -132,11 +131,11 @@ func (dts *dialectTestSuite) TestSupportsOrderByOnDelete() {
t := dts.T()
opts := DefaultDialectOptions()
opts.SupportsOrderByOnDelete = true
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.SupportsOrderByOnDelete = false
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
assert.True(t, d.SupportsOrderByOnDelete())
assert.False(t, d2.SupportsOrderByOnDelete())
@@ -146,11 +145,11 @@ func (dts *dialectTestSuite) TestSupportsLimitOnDelete() {
t := dts.T()
opts := DefaultDialectOptions()
opts.SupportsLimitOnDelete = true
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.SupportsLimitOnDelete = false
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
assert.True(t, d.SupportsLimitOnDelete())
assert.False(t, d2.SupportsLimitOnDelete())
@@ -160,11 +159,11 @@ func (dts *dialectTestSuite) TestUpdateBeginSQL() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.UpdateClause = []byte("update")
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
b := sb.NewSQLBuilder(false)
d.UpdateBeginSQL(b)
@@ -185,11 +184,11 @@ func (dts *dialectTestSuite) TestInsertBeginSQL() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.InsertClause = []byte("insert into")
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
b := sb.NewSQLBuilder(false)
d.InsertBeginSQL(b, nil)
@@ -211,12 +210,12 @@ func (dts *dialectTestSuite) TestInsertBeginSQL_WithConflictExpression() {
opts := DefaultDialectOptions()
opts.SupportsInsertIgnoreSyntax = true
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.SupportsInsertIgnoreSyntax = true
opts2.InsertIgnoreClause = []byte("insert ignore into")
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
ce := exp.NewDoNothingConflictExpression()
b := sb.NewSQLBuilder(false)
@@ -238,11 +237,11 @@ func (dts *dialectTestSuite) TestDeleteBeginSQL() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.DeleteClause = []byte("delete")
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
b := sb.NewSQLBuilder(false)
d.DeleteBeginSQL(b)
@@ -263,14 +262,14 @@ func (dts *dialectTestSuite) TestTruncateSQL() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.TruncateClause = []byte("truncate")
opts2.IdentityFragment = []byte(" identity")
opts2.CascadeFragment = []byte(" cascade")
opts2.RestrictFragment = []byte(" restrict")
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
cols := exp.NewColumnListExpression("a")
b := sb.NewSQLBuilder(false)
@@ -328,20 +327,18 @@ func (dts *dialectTestSuite) TestInsertSQL_empty() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
opts2.DefaultValuesFragment = []byte(" default values")
- d2 := sqlDialect{opts2}
-
- ie, err := exp.NewInsertExpression()
- assert.NoError(t, err)
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
+ ic := exp.NewInsertClauses()
b := sb.NewSQLBuilder(false)
- d.InsertSQL(b, ie)
+ d.InsertSQL(b, ic)
dts.assertNotPreparedSQL(t, b, " DEFAULT VALUES")
- d2.InsertSQL(b.Clear(), ie)
+ d2.InsertSQL(b.Clear(), ic)
dts.assertNotPreparedSQL(t, b, " default values")
}
@@ -349,23 +346,21 @@ func (dts *dialectTestSuite) TestInsertSQL_nilValues() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
opts2 := DefaultDialectOptions()
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
- ie, err := exp.NewInsertExpression()
- assert.NoError(t, err)
- ie = ie.SetCols(exp.NewColumnListExpression("a")).
+ ic := exp.NewInsertClauses().SetCols(exp.NewColumnListExpression("a")).
SetVals([][]interface{}{
{nil},
})
b := sb.NewSQLBuilder(false)
- d.InsertSQL(b, ie)
+ d.InsertSQL(b, ic)
dts.assertNotPreparedSQL(t, b, ` ("a") VALUES (NULL)`)
- d2.InsertSQL(b.Clear(), ie)
+ d2.InsertSQL(b.Clear(), ic)
dts.assertNotPreparedSQL(t, b, ` ("a") VALUES (NULL)`)
}
@@ -380,18 +375,17 @@ func (dts *dialectTestSuite) TestInsertSQL() {
opts.RightParenRune = '}'
opts.CommaRune = ';'
opts.PlaceHolderRune = '#'
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
- ie, err := exp.NewInsertExpression()
- assert.NoError(t, err)
- ie = ie.SetCols(exp.NewColumnListExpression("a", "b")).
+ ic := exp.NewInsertClauses().
+ SetCols(exp.NewColumnListExpression("a", "b")).
SetVals([][]interface{}{
{"a1", "b1"},
{"a2", "b2"},
{"a3", "b3"},
})
- bie := ie.SetCols(exp.NewColumnListExpression("a", "b")).
+ bic := ic.SetCols(exp.NewColumnListExpression("a", "b")).
SetVals([][]interface{}{
{"a1"},
{"a2", "b2"},
@@ -399,18 +393,109 @@ func (dts *dialectTestSuite) TestInsertSQL() {
})
b := sb.NewSQLBuilder(false)
- d.InsertSQL(b, ie)
+ d.InsertSQL(b, ic)
dts.assertNotPreparedSQL(t, b, ` {"a"; "b"} values {'a1'; 'b1'}; {'a2'; 'b2'}; {'a3'; 'b3'}`)
b = sb.NewSQLBuilder(true)
- d.InsertSQL(b, ie)
+ d.InsertSQL(b, ic)
dts.assertPreparedSQL(t, b, ` {"a"; "b"} values {#; #}; {#; #}; {#; #}`, []interface{}{
"a1", "b1", "a2", "b2", "a3", "b3",
})
- d.InsertSQL(b.Clear(), bie)
+ d.InsertSQL(b.Clear(), bic)
dts.assertErrorSQL(t, b, "goqu: rows with different value length expected 1 got 2")
+}
+
+func (dts *dialectTestSuite) TestInsertSQL_withRows() {
+ t := dts.T()
+
+ opts := DefaultDialectOptions()
+ opts.LeftParenRune = '{'
+ opts.RightParenRune = '}'
+ opts.ValuesFragment = []byte(" values ")
+ opts.LeftParenRune = '{'
+ opts.RightParenRune = '}'
+ opts.CommaRune = ';'
+ opts.PlaceHolderRune = '#'
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
+
+ ic := exp.NewInsertClauses().
+ SetRows([]interface{}{
+ exp.Record{"a": "a1", "b": "b1"},
+ exp.Record{"a": "a2", "b": "b2"},
+ exp.Record{"a": "a3", "b": "b3"},
+ })
+
+ bic := ic.
+ SetRows([]interface{}{
+ exp.Record{"a": "a1"},
+ exp.Record{"a": "a2", "b": "b2"},
+ exp.Record{"a": "a3", "b": "b3"},
+ })
+
+ b := sb.NewSQLBuilder(false)
+ d.InsertSQL(b, ic)
+ dts.assertNotPreparedSQL(t, b, ` {"a"; "b"} values {'a1'; 'b1'}; {'a2'; 'b2'}; {'a3'; 'b3'}`)
+ b = sb.NewSQLBuilder(true)
+ d.InsertSQL(b, ic)
+ dts.assertPreparedSQL(t, b, ` {"a"; "b"} values {#; #}; {#; #}; {#; #}`, []interface{}{
+ "a1", "b1", "a2", "b2", "a3", "b3",
+ })
+
+ d.InsertSQL(b.Clear(), bic)
+ dts.assertErrorSQL(t, b, "goqu: rows with different value length expected 1 got 2")
+}
+
+func (dts *dialectTestSuite) TestInsertSQL_colsWithFrom() {
+ t := dts.T()
+
+ opts := DefaultDialectOptions()
+ opts.LeftParenRune = '{'
+ opts.RightParenRune = '}'
+ opts.ValuesFragment = []byte(" values ")
+ opts.LeftParenRune = '{'
+ opts.RightParenRune = '}'
+ opts.CommaRune = ';'
+ opts.PlaceHolderRune = '#'
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
+
+ ic := exp.NewInsertClauses().
+ SetCols(exp.NewColumnListExpression("a", "b")).
+ SetFrom(newTestAppendableExpression("select c, d from test where a = 'b'", nil, nil, nil))
+
+ b := sb.NewSQLBuilder(false)
+ d.InsertSQL(b, ic)
+ dts.assertNotPreparedSQL(t, b, ` {"a"; "b"} select c, d from test where a = 'b'`)
+
+ b = sb.NewSQLBuilder(true)
+ d.InsertSQL(b, ic)
+ dts.assertPreparedSQL(t, b, ` {"a"; "b"} select c, d from test where a = 'b'`, emptyArgs)
+}
+
+func (dts *dialectTestSuite) TestInsertSQL_withFrom() {
+ t := dts.T()
+
+ opts := DefaultDialectOptions()
+ opts.LeftParenRune = '{'
+ opts.RightParenRune = '}'
+ opts.ValuesFragment = []byte(" values ")
+ opts.LeftParenRune = '{'
+ opts.RightParenRune = '}'
+ opts.CommaRune = ';'
+ opts.PlaceHolderRune = '#'
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
+
+ ic := exp.NewInsertClauses().
+ SetFrom(newTestAppendableExpression("select c, d from test where a = 'b'", nil, nil, nil))
+
+ b := sb.NewSQLBuilder(false)
+ d.InsertSQL(b, ic)
+ dts.assertNotPreparedSQL(t, b, ` select c, d from test where a = 'b'`)
+
+ b = sb.NewSQLBuilder(true)
+ d.InsertSQL(b, ic)
+ dts.assertPreparedSQL(t, b, ` select c, d from test where a = 'b'`, emptyArgs)
}
func (dts *dialectTestSuite) TestInsertSQL_onConflict() {
@@ -421,51 +506,50 @@ func (dts *dialectTestSuite) TestInsertSQL_onConflict() {
opts.ConflictFragment = []byte(" on conflict")
opts.ConflictDoNothingFragment = []byte(" do nothing")
opts.ConflictDoUpdateFragment = []byte(" do update set ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
- ienoc, err := exp.NewInsertExpression()
- assert.NoError(t, err)
- ienoc = ienoc.SetCols(exp.NewColumnListExpression("a")).
+ icnoc := exp.NewInsertClauses().
+ SetCols(exp.NewColumnListExpression("a")).
SetVals([][]interface{}{
{"a1"},
{"a2"},
{"a3"},
})
- iedn := ienoc.DoNothing()
- iedu := ienoc.DoUpdate("test", exp.Record{"a": "b"})
- iedoc := ienoc.DoUpdate("on constraint test", exp.Record{"a": "b"})
- ieduw := ienoc.SetOnConflict(
+ icdn := icnoc.SetOnConflict(DoNothing())
+ icdu := icnoc.SetOnConflict(DoUpdate("test", exp.Record{"a": "b"}))
+ icdoc := icnoc.SetOnConflict(DoUpdate("on constraint test", exp.Record{"a": "b"}))
+ icduw := icnoc.SetOnConflict(
exp.NewDoUpdateConflictExpression("test", exp.Record{"a": "b"}).Where(exp.Ex{"foo": true}),
)
b := sb.NewSQLBuilder(false)
- d.InsertSQL(b, ienoc)
+ d.InsertSQL(b, icnoc)
dts.assertNotPreparedSQL(t, b, ` ("a") VALUES ('a1'), ('a2'), ('a3')`)
- d.InsertSQL(b.Clear(), iedn)
+ d.InsertSQL(b.Clear(), icdn)
dts.assertNotPreparedSQL(t, b, ` ("a") VALUES ('a1'), ('a2'), ('a3') on conflict do nothing`)
- d.InsertSQL(b.Clear(), iedu)
+ d.InsertSQL(b.Clear(), icdu)
dts.assertNotPreparedSQL(
t,
b,
` ("a") VALUES ('a1'), ('a2'), ('a3') on conflict (test) do update set "a"='b'`,
)
- d.InsertSQL(b.Clear(), iedoc)
+ d.InsertSQL(b.Clear(), icdoc)
dts.assertNotPreparedSQL(t, b, ` ("a") VALUES ('a1'), ('a2'), ('a3') on conflict on constraint test do update set "a"='b'`)
- d.InsertSQL(b.Clear(), ieduw)
+ d.InsertSQL(b.Clear(), icduw)
dts.assertNotPreparedSQL(t, b, ` ("a") VALUES ('a1'), ('a2'), ('a3') on conflict (test) do update set "a"='b' WHERE ("foo" IS TRUE)`)
b = sb.NewSQLBuilder(true)
- d.InsertSQL(b, iedn)
+ d.InsertSQL(b, icdn)
dts.assertPreparedSQL(t, b, ` ("a") VALUES (?), (?), (?) on conflict do nothing`, []interface{}{
"a1", "a2", "a3",
})
- d.InsertSQL(b.Clear(), iedu)
+ d.InsertSQL(b.Clear(), icdu)
dts.assertPreparedSQL(
t,
b,
@@ -473,7 +557,7 @@ func (dts *dialectTestSuite) TestInsertSQL_onConflict() {
[]interface{}{"a1", "a2", "a3", "b"},
)
- d.InsertSQL(b.Clear(), ieduw)
+ d.InsertSQL(b.Clear(), icduw)
dts.assertPreparedSQL(
t,
b,
@@ -488,7 +572,7 @@ func (dts *dialectTestSuite) TestUpdateExpressionsSQL() {
opts := DefaultDialectOptions()
// make sure the fragments are used
opts.SetFragment = []byte(" set ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
u, err := exp.NewUpdateExpressions(exp.Record{"a": "b"})
assert.NoError(t, err)
@@ -508,7 +592,7 @@ func (dts *dialectTestSuite) TestSelectSQL() {
// make sure the fragments are used
opts.SelectClause = []byte("select")
opts.StarRune = '#'
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
ec := exp.NewColumnListExpression()
cs := exp.NewColumnListExpression("a", "b")
b := sb.NewSQLBuilder(false)
@@ -533,7 +617,7 @@ func (dts *dialectTestSuite) TestSelectDistinctSQL() {
// make sure the fragments are used
opts.SelectClause = []byte("select")
opts.DistinctFragment = []byte(" distinct ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
ec := exp.NewColumnListExpression()
cs := exp.NewColumnListExpression("a", "b")
b := sb.NewSQLBuilder(false)
@@ -557,7 +641,7 @@ func (dts *dialectTestSuite) TestReturningSQL() {
opts := DefaultDialectOptions()
// make sure the fragments are used
opts.ReturningFragment = []byte(" returning ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
ec := exp.NewColumnListExpression()
cs := exp.NewColumnListExpression("a", "b")
b := sb.NewSQLBuilder(false)
@@ -581,7 +665,7 @@ func (dts *dialectTestSuite) TestFromSQL() {
opts := DefaultDialectOptions()
// make sure the fragments are used
opts.FromFragment = []byte(" from")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
ec := exp.NewColumnListExpression()
cs := exp.NewColumnListExpression("a", "b")
b := sb.NewSQLBuilder(false)
@@ -603,7 +687,7 @@ func (dts *dialectTestSuite) TestSourcesSQL() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
ec := exp.NewColumnListExpression()
cs := exp.NewColumnListExpression("a", "b")
b := sb.NewSQLBuilder(false)
@@ -625,7 +709,7 @@ func (dts *dialectTestSuite) TestJoinSQL() {
t := dts.T()
opts := DefaultDialectOptions()
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
ti := exp.NewIdentifierExpression("", "test", "")
uj := exp.NewUnConditionedJoinExpression(exp.NaturalJoinType, ti)
cjo := exp.NewConditionedJoinExpression(exp.LeftJoinType, ti, exp.NewJoinOnCondition(exp.Ex{"a": "foo"}))
@@ -673,7 +757,7 @@ func (dts *dialectTestSuite) TestJoinSQL() {
exp.LeftJoinType: []byte(" left join "),
exp.NaturalJoinType: []byte(" natural join "),
}
- d2 := sqlDialect{opts2}
+ d2 := sqlDialect{dialect: "test", dialectOptions: opts2}
b = sb.NewSQLBuilder(false)
d2.JoinSQL(b.Clear(), exp.JoinExpressions{uj})
@@ -702,7 +786,7 @@ func (dts *dialectTestSuite) TestWhereSQL() {
opts := DefaultDialectOptions()
opts.WhereFragment = []byte(" where ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
w := exp.Ex{"a": "b"}
w2 := exp.Ex{"b": "c"}
@@ -729,7 +813,7 @@ func (dts *dialectTestSuite) TestGroupBySQL() {
opts := DefaultDialectOptions()
opts.GroupByFragment = []byte(" group by ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
c1 := exp.NewIdentifierExpression("", "", "a")
c2 := exp.NewIdentifierExpression("", "", "b")
@@ -755,7 +839,7 @@ func (dts *dialectTestSuite) TestHavingSQL() {
opts := DefaultDialectOptions()
opts.HavingFragment = []byte(" having ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
w := exp.Ex{"a": "b"}
w2 := exp.Ex{"b": "c"}
@@ -787,7 +871,7 @@ func (dts *dialectTestSuite) TestOrderSQL() {
opts.DescFragment = []byte(" desc")
opts.NullsFirstFragment = []byte(" nulls first")
opts.NullsLastFragment = []byte(" nulls last")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
oa := exp.NewIdentifierExpression("", "", "a").Asc()
oanf := exp.NewIdentifierExpression("", "", "a").Asc().NullsFirst()
@@ -846,7 +930,7 @@ func (dts *dialectTestSuite) TestLimitSQL() {
opts := DefaultDialectOptions()
opts.LimitFragment = []byte(" limit ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
b := sb.NewSQLBuilder(false)
d.LimitSQL(b, 10)
@@ -879,7 +963,7 @@ func (dts *dialectTestSuite) TestOffsetSQL() {
opts := DefaultDialectOptions()
opts.OffsetFragment = []byte(" offset ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
o := uint(10)
b := sb.NewSQLBuilder(false)
@@ -903,7 +987,7 @@ func (dts *dialectTestSuite) TestCommonTablesSQL() {
opts := DefaultDialectOptions()
opts.WithFragment = []byte("with ")
opts.RecursiveFragment = []byte("recursive ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil)
cte1 := exp.NewCommonTableExpression(false, "test_cte", tse)
cte2 := exp.NewCommonTableExpression(true, "test_cte", tse)
@@ -927,14 +1011,14 @@ func (dts *dialectTestSuite) TestCommonTablesSQL() {
opts = DefaultDialectOptions()
opts.SupportsWithCTE = false
- d = sqlDialect{opts}
+ d = sqlDialect{dialect: "test", dialectOptions: opts}
d.CommonTablesSQL(b.Clear(), []exp.CommonTableExpression{cte1})
dts.assertErrorSQL(t, b, "goqu: adapter does not support CTE with clause")
opts = DefaultDialectOptions()
opts.SupportsWithCTERecursive = false
- d = sqlDialect{opts}
+ d = sqlDialect{dialect: "test", dialectOptions: opts}
d.CommonTablesSQL(b.Clear(), []exp.CommonTableExpression{cte2})
dts.assertErrorSQL(t, b, "goqu: adapter does not support CTE with recursive clause")
@@ -952,7 +1036,7 @@ func (dts *dialectTestSuite) TestCompoundsSQL() {
opts.UnionAllFragment = []byte(" union all ")
opts.IntersectFragment = []byte(" intersect ")
opts.IntersectAllFragment = []byte(" intersect all ")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
tse := newTestAppendableExpression("select * from foo", emptyArgs, nil, nil)
u := exp.NewCompoundExpression(exp.UnionCompoundType, tse)
ua := exp.NewCompoundExpression(exp.UnionAllCompoundType, tse)
@@ -994,7 +1078,7 @@ func (dts *dialectTestSuite) TestForSQL() {
opts.ForKeyShareFragment = []byte(" for key share ")
opts.NowaitFragment = []byte("nowait")
opts.SkipLockedFragment = []byte("skip locked")
- d := sqlDialect{opts}
+ d := sqlDialect{dialect: "test", dialectOptions: opts}
b := sb.NewSQLBuilder(false)
d.ForSQL(b.Clear(), exp.NewLock(exp.ForNolock, exp.Wait))
@@ -1043,7 +1127,7 @@ func (dts *dialectTestSuite) TestForSQL() {
func (dts *dialectTestSuite) TestLiteral_FloatTypes() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
var float float64
b := sb.NewSQLBuilder(false)
@@ -1069,7 +1153,7 @@ func (dts *dialectTestSuite) TestLiteral_FloatTypes() {
func (dts *dialectTestSuite) TestLiteral_IntTypes() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
var i int64
b := sb.NewSQLBuilder(false)
ints := []interface{}{
@@ -1100,7 +1184,7 @@ func (dts *dialectTestSuite) TestLiteral_IntTypes() {
func (dts *dialectTestSuite) TestLiteral_StringTypes() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
var str string
b := sb.NewSQLBuilder(false)
@@ -1128,7 +1212,7 @@ func (dts *dialectTestSuite) TestLiteral_StringTypes() {
func (dts *dialectTestSuite) TestLiteral_BytesTypes() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), []byte("Hello"))
@@ -1151,7 +1235,7 @@ func (dts *dialectTestSuite) TestLiteral_BoolTypes() {
t := dts.T()
var bl bool
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), true)
@@ -1176,7 +1260,7 @@ func (dts *dialectTestSuite) TestLiteral_BoolTypes() {
func (dts *dialectTestSuite) TestLiteral_TimeTypes() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "default", dialectOptions: DefaultDialectOptions()}
var nt *time.Time
asiaShanghai, err := time.LoadLocation("Asia/Shanghai")
require.NoError(t, err)
@@ -1211,7 +1295,7 @@ func (dts *dialectTestSuite) TestLiteral_TimeTypes() {
func (dts *dialectTestSuite) TestLiteral_NilTypes() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), nil)
dts.assertNotPreparedSQL(t, b, "NULL")
@@ -1230,7 +1314,7 @@ func (j datasetValuerType) Value() (driver.Value, error) {
func (dts *dialectTestSuite) TestLiteral_Valuer() {
t := dts.T()
b := sb.NewSQLBuilder(false)
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
d.Literal(b.Clear(), datasetValuerType(10))
dts.assertNotPreparedSQL(t, b, "'Hello World 10'")
@@ -1243,7 +1327,7 @@ func (dts *dialectTestSuite) TestLiteral_Valuer() {
func (dts *dialectTestSuite) TestLiteral_Slice() {
t := dts.T()
b := sb.NewSQLBuilder(false)
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
d.Literal(b.Clear(), []string{"a", "b", "c"})
dts.assertNotPreparedSQL(t, b, `('a', 'b', 'c')`)
@@ -1263,7 +1347,7 @@ func (ue unknownExpression) Clone() exp.Expression {
}
func (dts *dialectTestSuite) TestLiteralUnsupportedExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), unknownExpression{})
dts.assertErrorSQL(t, b, "goqu: unsupported expression type goqu.unknownExpression")
@@ -1271,11 +1355,11 @@ func (dts *dialectTestSuite) TestLiteralUnsupportedExpression() {
func (dts *dialectTestSuite) TestLiteral_AppendableExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
ti := exp.NewIdentifierExpression("", "b", "")
a := newTestAppendableExpression(`select * from "a"`, []interface{}{}, nil, nil)
- aliasedA := newTestAppendableExpression(`select * from "a"`, []interface{}{}, nil, exp.NewClauses().SetAlias(ti))
- argsA := newTestAppendableExpression(`select * from "a" where x=?`, []interface{}{true}, nil, exp.NewClauses().SetAlias(ti))
+ aliasedA := newTestAppendableExpression(`select * from "a"`, []interface{}{}, nil, exp.NewSelectClauses().SetAlias(ti))
+ argsA := newTestAppendableExpression(`select * from "a" where x=?`, []interface{}{true}, nil, exp.NewSelectClauses().SetAlias(ti))
ae := newTestAppendableExpression(`select * from "a"`, emptyArgs, errors.New("expected error"), nil)
b := sb.NewSQLBuilder(false)
@@ -1301,7 +1385,7 @@ func (dts *dialectTestSuite) TestLiteral_AppendableExpression() {
func (dts *dialectTestSuite) TestLiteral_ColumnList() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewColumnListExpression("a", exp.NewLiteralExpression("true")))
@@ -1314,7 +1398,7 @@ func (dts *dialectTestSuite) TestLiteral_ColumnList() {
func (dts *dialectTestSuite) TestLiteral_ExpressionList() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewExpressionList(
@@ -1369,7 +1453,7 @@ func (dts *dialectTestSuite) TestLiteral_ExpressionList() {
func (dts *dialectTestSuite) TestLiteral_LiteralExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewLiteralExpression(`"b"::DATE = '2010-09-02'`))
@@ -1401,7 +1485,7 @@ func (dts *dialectTestSuite) TestLiteral_LiteralExpression() {
func (dts *dialectTestSuite) TestLiteral_AliasedExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewIdentifierExpression("", "", "a").As("b"))
@@ -1424,7 +1508,7 @@ func (dts *dialectTestSuite) TestLiteral_AliasedExpression() {
func (dts *dialectTestSuite) TestLiteral_BooleanExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
ae := newTestAppendableExpression(`SELECT "id" FROM "test2"`, emptyArgs, nil, nil)
@@ -1627,7 +1711,7 @@ func (dts *dialectTestSuite) TestLiteral_BooleanExpression() {
func (dts *dialectTestSuite) TestLiteral_RangeExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewIdentifierExpression("", "", "a").
@@ -1658,7 +1742,7 @@ func (dts *dialectTestSuite) TestLiteral_RangeExpression() {
func (dts *dialectTestSuite) TestLiteral_OrderedExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewIdentifierExpression("", "", "a").Asc())
@@ -1701,7 +1785,7 @@ func (dts *dialectTestSuite) TestLiteral_OrderedExpression() {
func (dts *dialectTestSuite) TestLiteral_UpdateExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewIdentifierExpression("", "", "a").Set(1))
@@ -1714,7 +1798,7 @@ func (dts *dialectTestSuite) TestLiteral_UpdateExpression() {
func (dts *dialectTestSuite) TestLiteral_SQLFunctionExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewSQLFunctionExpression("MIN", exp.NewIdentifierExpression("", "", "a")))
@@ -1734,7 +1818,7 @@ func (dts *dialectTestSuite) TestLiteral_SQLFunctionExpression() {
func (dts *dialectTestSuite) TestLiteral_CastExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewIdentifierExpression("", "", "a").Cast("DATE"))
dts.assertNotPreparedSQL(t, b, `CAST("a" AS DATE)`)
@@ -1746,7 +1830,7 @@ func (dts *dialectTestSuite) TestLiteral_CastExpression() {
func (dts *dialectTestSuite) TestLiteral_CommonTableExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
ae := newTestAppendableExpression(`SELECT * FROM "b"`, emptyArgs, nil, nil)
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewCommonTableExpression(false, "a", ae))
@@ -1780,7 +1864,7 @@ func (dts *dialectTestSuite) TestLiteral_CompoundExpression() {
ae := newTestAppendableExpression(`SELECT * FROM "b"`, emptyArgs, nil, nil)
b := sb.NewSQLBuilder(false)
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
d.Literal(b.Clear(), exp.NewCompoundExpression(exp.UnionCompoundType, ae))
dts.assertNotPreparedSQL(t, b, ` UNION (SELECT * FROM "b")`)
@@ -1810,7 +1894,7 @@ func (dts *dialectTestSuite) TestLiteral_CompoundExpression() {
func (dts *dialectTestSuite) TestLiteral_IdentifierExpression() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.NewIdentifierExpression("", "", "col"))
@@ -1865,7 +1949,7 @@ func (dts *dialectTestSuite) TestLiteral_IdentifierExpression() {
func (dts *dialectTestSuite) TestLiteral_ExpressionMap() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.Ex{"a": 1})
@@ -1986,7 +2070,7 @@ func (dts *dialectTestSuite) TestLiteral_ExpressionMap() {
func (dts *dialectTestSuite) TestLiteral_ExpressionOrMap() {
t := dts.T()
- d := sqlDialect{DefaultDialectOptions()}
+ d := sqlDialect{dialect: "test", dialectOptions: DefaultDialectOptions()}
b := sb.NewSQLBuilder(false)
d.Literal(b.Clear(), exp.ExOr{"a": 1, "b": true})
diff --git a/truncate_dataset.go b/truncate_dataset.go
new file mode 100644
index 00000000..ba63403c
--- /dev/null
+++ b/truncate_dataset.go
@@ -0,0 +1,148 @@
+package goqu
+
+import (
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+)
+
+type TruncateDataset struct {
+ dialect SQLDialect
+ clauses exp.TruncateClauses
+ isPrepared bool
+ queryFactory exec.QueryFactory
+}
+
+// used internally by database to create a database with a specific adapter
+func newTruncateDataset(d string, queryFactory exec.QueryFactory) *TruncateDataset {
+ return &TruncateDataset{
+ clauses: exp.NewTruncateClauses(),
+ dialect: GetDialect(d),
+ queryFactory: queryFactory,
+ }
+}
+
+func Truncate(table ...interface{}) *TruncateDataset {
+ return newTruncateDataset("default", nil).Table(table...)
+}
+
+// Sets the adapter used to serialize values and create the SQL statement
+func (td *TruncateDataset) WithDialect(dl string) *TruncateDataset {
+ ds := td.copy(td.GetClauses())
+ ds.dialect = GetDialect(dl)
+ return ds
+}
+
+// Set the parameter interpolation behavior. See examples
+//
+// 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
+ return ret
+}
+
+func (td *TruncateDataset) IsPrepared() bool {
+ return td.isPrepared
+}
+
+// Returns the current adapter on the dataset
+func (td *TruncateDataset) Dialect() SQLDialect {
+ return td.dialect
+}
+
+// Returns the current adapter on the dataset
+func (td *TruncateDataset) SetDialect(dialect SQLDialect) *TruncateDataset {
+ cd := td.copy(td.GetClauses())
+ cd.dialect = dialect
+ return cd
+}
+
+func (td *TruncateDataset) Expression() exp.Expression {
+ return td
+}
+
+// Clones the dataset
+func (td *TruncateDataset) Clone() exp.Expression {
+ return td.copy(td.clauses)
+}
+
+// Returns the current clauses on the dataset.
+func (td *TruncateDataset) GetClauses() exp.TruncateClauses {
+ return td.clauses
+}
+
+// used interally to copy the dataset
+func (td *TruncateDataset) copy(clauses exp.TruncateClauses) *TruncateDataset {
+ return &TruncateDataset{
+ dialect: td.dialect,
+ clauses: clauses,
+ isPrepared: td.isPrepared,
+ queryFactory: td.queryFactory,
+ }
+}
+
+// Adds a FROM clause. This return a new dataset with the original sources replaced. See examples.
+// You can pass in the following.
+// string: Will automatically be turned into an identifier
+// IdentifierExpression
+// LiteralExpression: (See Literal) Will use the literal SQL
+func (td *TruncateDataset) Table(table ...interface{}) *TruncateDataset {
+ return td.copy(td.clauses.SetTable(exp.NewColumnListExpression(table...)))
+}
+
+// Adds a CASCADE clause
+func (td *TruncateDataset) Cascade() *TruncateDataset {
+ opts := td.clauses.Options()
+ opts.Cascade = true
+ return td.copy(td.clauses.SetOptions(opts))
+}
+
+// Clears the CASCADE clause
+func (td *TruncateDataset) NoCascade() *TruncateDataset {
+ opts := td.clauses.Options()
+ opts.Cascade = false
+ return td.copy(td.clauses.SetOptions(opts))
+}
+
+// Adds a RESTRICT clause
+func (td *TruncateDataset) Restrict() *TruncateDataset {
+ opts := td.clauses.Options()
+ opts.Restrict = true
+ return td.copy(td.clauses.SetOptions(opts))
+}
+
+// Clears the RESTRICT clause
+func (td *TruncateDataset) NoRestrict() *TruncateDataset {
+ opts := td.clauses.Options()
+ opts.Restrict = false
+ return td.copy(td.clauses.SetOptions(opts))
+}
+
+// Add a IDENTITY clause (e.g. RESTART)
+func (td *TruncateDataset) Identity(identity string) *TruncateDataset {
+ opts := td.clauses.Options()
+ opts.Identity = identity
+ return td.copy(td.clauses.SetOptions(opts))
+}
+
+// Generates a TRUNCATE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
+// See examples.
+//
+// Errors:
+// * There is an error generating the SQL
+func (td *TruncateDataset) ToSQL() (sql string, params []interface{}, err error) {
+ return td.truncateSQLBuilder().ToSQL()
+}
+
+// Generates the TRUNCATE sql, and returns an Exec struct with the sql set to the TRUNCATE statement
+// db.From("test").Truncate().Executor().Exec()
+func (td *TruncateDataset) Executor() exec.QueryExecutor {
+ return td.queryFactory.FromSQLBuilder(td.truncateSQLBuilder())
+}
+
+func (td *TruncateDataset) truncateSQLBuilder() sb.SQLBuilder {
+ buf := sb.NewSQLBuilder(td.isPrepared)
+ td.dialect.ToTruncateSQL(buf, td.clauses)
+ return buf
+}
diff --git a/truncate_dataset_test.go b/truncate_dataset_test.go
new file mode 100644
index 00000000..b3ce36b8
--- /dev/null
+++ b/truncate_dataset_test.go
@@ -0,0 +1,216 @@
+package goqu
+
+import (
+ "testing"
+
+ "github.com/DATA-DOG/go-sqlmock"
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+ "github.com/doug-martin/goqu/v8/mocks"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+)
+
+type truncateDatasetSuite struct {
+ suite.Suite
+}
+
+func (tds *truncateDatasetSuite) TestClone() {
+ t := tds.T()
+ ds := Truncate("test")
+ assert.Equal(t, ds.Clone(), ds)
+}
+
+func (tds *truncateDatasetSuite) TestExpression() {
+ t := tds.T()
+ ds := Truncate("test")
+ assert.Equal(t, ds.Expression(), ds)
+}
+
+func (tds *truncateDatasetSuite) TestDialect() {
+ t := tds.T()
+ ds := Truncate("test")
+ assert.NotNil(t, ds.Dialect())
+}
+
+func (tds *truncateDatasetSuite) TestWithDialect() {
+ t := tds.T()
+ ds := Truncate("test")
+ dialect := GetDialect("default")
+ ds.WithDialect("default")
+ assert.Equal(t, ds.Dialect(), dialect)
+}
+
+func (tds *truncateDatasetSuite) TestPrepared() {
+ t := tds.T()
+ ds := Truncate("test")
+ preparedDs := ds.Prepared(true)
+ assert.True(t, preparedDs.IsPrepared())
+ assert.False(t, ds.IsPrepared())
+ // should apply the prepared to any datasets created from the root
+ assert.True(t, preparedDs.Restrict().IsPrepared())
+}
+
+func (tds *truncateDatasetSuite) TestGetClauses() {
+ t := tds.T()
+ ds := Truncate("test")
+ ce := exp.NewTruncateClauses().SetTable(exp.NewColumnListExpression(I("test")))
+ assert.Equal(t, ce, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestTable(from ...interface{}) {
+ t := tds.T()
+ ds := Truncate("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetTable(exp.NewColumnListExpression(T("t")))
+ assert.Equal(t, ec, ds.Table(T("t")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestCascade() {
+ t := tds.T()
+ ds := Truncate("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetOptions(exp.TruncateOptions{Cascade: true})
+ assert.Equal(t, ec, ds.Cascade().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestCascade_ToSQL() {
+ t := tds.T()
+ ds1 := Truncate("items")
+ tsql, _, err := ds1.Cascade().ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, tsql, `TRUNCATE "items" CASCADE`)
+}
+
+func (tds *truncateDatasetSuite) TestNoCascade() {
+ t := tds.T()
+ ds := Truncate("test").Cascade()
+ dsc := ds.GetClauses()
+ ec := dsc.SetOptions(exp.TruncateOptions{Cascade: false})
+ assert.Equal(t, ec, ds.NoCascade().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestRestrict() {
+ t := tds.T()
+ ds := Truncate("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetOptions(exp.TruncateOptions{Restrict: true})
+ assert.Equal(t, ec, ds.Restrict().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestRestrict_ToSQL() {
+ t := tds.T()
+ ds1 := Truncate("items")
+ tsql, _, err := ds1.Restrict().ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, tsql, `TRUNCATE "items" RESTRICT`)
+}
+
+func (tds *truncateDatasetSuite) TestNoRestrict() {
+ t := tds.T()
+ ds := Truncate("test").Restrict()
+ dsc := ds.GetClauses()
+ ec := dsc.SetOptions(exp.TruncateOptions{Restrict: false})
+ assert.Equal(t, ec, ds.NoRestrict().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestIdentity() {
+ t := tds.T()
+ ds := Truncate("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetOptions(exp.TruncateOptions{Identity: "RESTART"})
+ assert.Equal(t, ec, ds.Identity("RESTART").GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (tds *truncateDatasetSuite) TestIdentity_ToSQL() {
+ t := tds.T()
+ ds1 := Truncate("items")
+
+ tsql, _, err := ds1.Identity("restart").ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, tsql, `TRUNCATE "items" RESTART IDENTITY`)
+
+ tsql, _, err = ds1.Identity("continue").ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, tsql, `TRUNCATE "items" CONTINUE IDENTITY`)
+}
+
+func (tds *truncateDatasetSuite) TestToSQL() {
+ t := tds.T()
+ md := new(mocks.SQLDialect)
+ ds := Truncate("test").SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToTruncateSQL", sqlB, c).Return(nil).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ md.AssertExpectations(t)
+}
+
+func (tds *truncateDatasetSuite) TestToSQL__withPrepared() {
+ t := tds.T()
+ md := new(mocks.SQLDialect)
+ ds := Truncate("test").Prepared(true).SetDialect(md)
+ c := ds.GetClauses()
+ sqlB := sb.NewSQLBuilder(true)
+ md.On("ToTruncateSQL", sqlB, c).Return(nil).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (tds *truncateDatasetSuite) TestToSQL_withError() {
+ t := tds.T()
+ md := new(mocks.SQLDialect)
+ ds := Truncate("test").SetDialect(md)
+ c := ds.GetClauses()
+ ee := errors.New("expected error")
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToTruncateSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(ee)
+ }).Once()
+
+ sql, args, err := ds.ToSQL()
+ assert.Empty(t, sql)
+ assert.Empty(t, args)
+ assert.Equal(t, ee, err)
+ md.AssertExpectations(t)
+}
+
+func (tds *truncateDatasetSuite) TestExecutor() {
+ t := tds.T()
+ mDb, _, err := sqlmock.New()
+ assert.NoError(t, err)
+
+ ds := newTruncateDataset("mock", exec.NewQueryFactory(mDb)).
+ Table("table1", "table2")
+
+ tsql, args, err := ds.Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `TRUNCATE "table1", "table2"`, tsql)
+
+ tsql, args, err = ds.Prepared(true).Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `TRUNCATE "table1", "table2"`, tsql)
+}
+
+func TestTruncateDataset(t *testing.T) {
+ suite.Run(t, new(truncateDatasetSuite))
+}
diff --git a/update_dataset.go b/update_dataset.go
new file mode 100644
index 00000000..852f8fd2
--- /dev/null
+++ b/update_dataset.go
@@ -0,0 +1,198 @@
+package goqu
+
+import (
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+)
+
+type UpdateDataset struct {
+ dialect SQLDialect
+ clauses exp.UpdateClauses
+ isPrepared bool
+ queryFactory exec.QueryFactory
+}
+
+// used internally by database to create a database with a specific adapter
+func newUpdateDataset(d string, queryFactory exec.QueryFactory) *UpdateDataset {
+ return &UpdateDataset{
+ clauses: exp.NewUpdateClauses(),
+ dialect: GetDialect(d),
+ queryFactory: queryFactory,
+ }
+}
+
+func Update(table interface{}) *UpdateDataset {
+ return newUpdateDataset("default", nil).Table(table)
+}
+
+// Set the parameter interpolation behavior. See examples
+//
+// 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
+ return ret
+}
+
+func (ud *UpdateDataset) IsPrepared() bool {
+ return ud.isPrepared
+}
+
+// Sets the adapter used to serialize values and create the SQL statement
+func (ud *UpdateDataset) WithDialect(dl string) *UpdateDataset {
+ ds := ud.copy(ud.GetClauses())
+ ds.dialect = GetDialect(dl)
+ return ds
+}
+
+// Returns the current adapter on the dataset
+func (ud *UpdateDataset) Dialect() SQLDialect {
+ return ud.dialect
+}
+
+// Returns the current adapter on the dataset
+func (ud *UpdateDataset) SetDialect(dialect SQLDialect) *UpdateDataset {
+ cd := ud.copy(ud.GetClauses())
+ cd.dialect = dialect
+ return cd
+}
+
+func (ud *UpdateDataset) Expression() exp.Expression {
+ return ud
+}
+
+// Clones the dataset
+func (ud *UpdateDataset) Clone() exp.Expression {
+ return ud.copy(ud.clauses)
+}
+
+// Returns the current clauses on the dataset.
+func (ud *UpdateDataset) GetClauses() exp.UpdateClauses {
+ return ud.clauses
+}
+
+// used internally to copy the dataset
+func (ud *UpdateDataset) copy(clauses exp.UpdateClauses) *UpdateDataset {
+ return &UpdateDataset{
+ dialect: ud.dialect,
+ clauses: clauses,
+ isPrepared: ud.isPrepared,
+ queryFactory: ud.queryFactory,
+ }
+}
+
+// Creates a WITH clause for a common table expression (CTE).
+//
+// The name will be available to use in the UPDATE from in the associated query; and can optionally
+// contain a list of column names "name(col1, col2, col3)".
+//
+// The name will refer to the results of the specified subquery.
+func (ud *UpdateDataset) With(name string, subquery exp.Expression) *UpdateDataset {
+ return ud.copy(ud.clauses.CommonTablesAppend(exp.NewCommonTableExpression(false, name, subquery)))
+}
+
+// Creates a WITH RECURSIVE clause for a common table expression (CTE)
+//
+// The name will be available to use in the UPDATE from in the associated query; and must
+// contain a list of column names "name(col1, col2, col3)" for a recursive clause.
+//
+// The name will refer to the results of the specified subquery. The subquery for
+// a recursive query will always end with a UNION or UNION ALL with a clause that
+// refers to the CTE by name.
+func (ud *UpdateDataset) WithRecursive(name string, subquery exp.Expression) *UpdateDataset {
+ return ud.copy(ud.clauses.CommonTablesAppend(exp.NewCommonTableExpression(true, name, subquery)))
+}
+
+// Sets the table to update.
+func (ud *UpdateDataset) Table(table interface{}) *UpdateDataset {
+ switch t := table.(type) {
+ case exp.Expression:
+ return ud.copy(ud.clauses.SetTable(t))
+ case string:
+ return ud.copy(ud.clauses.SetTable(exp.ParseIdentifier(t)))
+ default:
+ panic("unsupported table type, a string or identifier expression is required")
+ }
+}
+
+// Sets the values to use in the SET clause. See examples.
+func (ud *UpdateDataset) Set(values interface{}) *UpdateDataset {
+ return ud.copy(ud.clauses.SetSetValues(values))
+}
+
+// Adds a WHERE clause. See examples.
+func (ud *UpdateDataset) Where(expressions ...exp.Expression) *UpdateDataset {
+ return ud.copy(ud.clauses.WhereAppend(expressions...))
+}
+
+// Removes the WHERE clause. See examples.
+func (ud *UpdateDataset) ClearWhere() *UpdateDataset {
+ return ud.copy(ud.clauses.ClearWhere())
+}
+
+// Adds a ORDER clause. If the ORDER is currently set it replaces it. See examples.
+func (ud *UpdateDataset) Order(order ...exp.OrderedExpression) *UpdateDataset {
+ return ud.copy(ud.clauses.SetOrder(order...))
+}
+
+// Adds a more columns to the current ORDER BY clause. If no order has be previously specified it is the same as
+// calling Order. See examples.
+func (ud *UpdateDataset) OrderAppend(order ...exp.OrderedExpression) *UpdateDataset {
+ return ud.copy(ud.clauses.OrderAppend(order...))
+}
+
+// Adds a more columns to the beginning of the current ORDER BY clause. If no order has be previously specified it is the same as
+// calling Order. See examples.
+func (ud *UpdateDataset) OrderPrepend(order ...exp.OrderedExpression) *UpdateDataset {
+ return ud.copy(ud.clauses.OrderPrepend(order...))
+}
+
+// Removes the ORDER BY clause. See examples.
+func (ud *UpdateDataset) ClearOrder() *UpdateDataset {
+ return ud.copy(ud.clauses.ClearOrder())
+}
+
+// Adds a LIMIT clause. If the LIMIT is currently set it replaces it. See examples.
+func (ud *UpdateDataset) Limit(limit uint) *UpdateDataset {
+ if limit > 0 {
+ return ud.copy(ud.clauses.SetLimit(limit))
+ }
+ return ud.copy(ud.clauses.ClearLimit())
+}
+
+// Adds a LIMIT ALL clause. If the LIMIT is currently set it replaces it. See examples.
+func (ud *UpdateDataset) LimitAll() *UpdateDataset {
+ return ud.copy(ud.clauses.SetLimit(L("ALL")))
+}
+
+// Removes the LIMIT clause.
+func (ud *UpdateDataset) ClearLimit() *UpdateDataset {
+ return ud.copy(ud.clauses.ClearLimit())
+}
+
+// Adds a RETURNING clause to the dataset if the adapter supports it. See examples.
+func (ud *UpdateDataset) Returning(returning ...interface{}) *UpdateDataset {
+ return ud.copy(ud.clauses.SetReturning(exp.NewColumnListExpression(returning...)))
+}
+
+// Generates an UPDATE sql statement, if Prepared has been called with true then the parameters will not be interpolated.
+// See examples.
+//
+// Errors:
+// * There is an error generating the SQL
+func (ud *UpdateDataset) ToSQL() (sql string, params []interface{}, err error) {
+ return ud.updateSQLBuilder().ToSQL()
+}
+
+// Generates the UPDATE sql, and returns an exec.QueryExecutor with the sql set to the UPDATE statement
+// db.Update("test").Set(Record{"name":"Bob", update: time.Now()}).Executor()
+func (ud *UpdateDataset) Executor() exec.QueryExecutor {
+ return ud.queryFactory.FromSQLBuilder(ud.updateSQLBuilder())
+}
+
+func (ud *UpdateDataset) updateSQLBuilder() sb.SQLBuilder {
+ buf := sb.NewSQLBuilder(ud.isPrepared)
+ ud.dialect.ToUpdateSQL(buf, ud.clauses)
+ return buf
+}
diff --git a/update_dataset_example_test.go b/update_dataset_example_test.go
new file mode 100644
index 00000000..93ced8b7
--- /dev/null
+++ b/update_dataset_example_test.go
@@ -0,0 +1,511 @@
+// nolint:lll
+package goqu_test
+
+import (
+ "fmt"
+
+ "github.com/doug-martin/goqu/v8"
+ _ "github.com/doug-martin/goqu/v8/dialect/mysql"
+)
+
+func ExampleUpdate_withStruct() {
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdate_withGoquRecord() {
+ sql, args, _ := goqu.Update("items").Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdate_withMap() {
+ sql, args, _ := goqu.Update("items").Set(
+ map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdate_withSkipUpdateTag() {
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name" goqu:"skipupdate"`
+ }
+ sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr' []
+}
+
+func ExampleUpdateDataset_Executor() {
+ db := getDb()
+ update := db.Update("goqu_user").
+ Where(goqu.C("first_name").Eq("Bob")).
+ Set(goqu.Record{"first_name": "Bobby"}).
+ Executor()
+
+ if r, err := update.Exec(); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ c, _ := r.RowsAffected()
+ fmt.Printf("Updated %d users", c)
+ }
+
+ // Output:
+ // Updated 1 users
+}
+
+func ExampleUpdateDataset_Executor_returning() {
+ db := getDb()
+ var ids []int64
+ update := db.Update("goqu_user").
+ Set(goqu.Record{"last_name": "ucon"}).
+ Where(goqu.Ex{"last_name": "Yukon"}).
+ Returning("id").
+ Executor()
+ if err := update.ScanVals(&ids); err != nil {
+ fmt.Println(err.Error())
+ } else {
+ fmt.Printf("Updated users with ids %+v", ids)
+ }
+
+ // Output:
+ // Updated users with ids [1 2 3]
+}
+
+func ExampleUpdateDataset_Returning() {
+ sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Returning("id").
+ ToSQL()
+ fmt.Println(sql)
+ sql, _, _ = goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Returning(goqu.T("test").All()).
+ ToSQL()
+ fmt.Println(sql)
+ sql, _, _ = goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Returning("a", "b").
+ ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE "test" SET "foo"='bar' RETURNING "id"
+ // UPDATE "test" SET "foo"='bar' RETURNING "test".*
+ // UPDATE "test" SET "foo"='bar' RETURNING "a", "b"
+}
+
+func ExampleUpdateDataset_With() {
+ sql, _, _ := goqu.Update("test").
+ With("some_vals(val)", goqu.From().Select(goqu.L("123"))).
+ Where(goqu.C("val").Eq(goqu.From("some_vals").Select("val"))).
+ Set(goqu.Record{"name": "Test"}).ToSQL()
+ fmt.Println(sql)
+
+ // Output:
+ // WITH some_vals(val) AS (SELECT 123) UPDATE "test" SET "name"='Test' WHERE ("val" IN (SELECT "val" FROM "some_vals"))
+}
+func ExampleUpdateDataset_WithRecursive() {
+ sql, _, _ := goqu.Update("nums").
+ WithRecursive("nums(x)", goqu.From().Select(goqu.L("1").As("num")).
+ UnionAll(goqu.From("nums").
+ Select(goqu.L("x+1").As("num")).Where(goqu.C("x").Lt(5)))).
+ Set(goqu.Record{"foo": goqu.T("nums").Col("num")}).
+ ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // WITH RECURSIVE nums(x) AS (SELECT 1 AS "num" UNION ALL (SELECT x+1 AS "num" FROM "nums" WHERE ("x" < 5))) UPDATE "nums" SET "foo"="nums"."num"
+}
+
+func ExampleUpdateDataset_Limit() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Limit(10)
+ sql, _, _ := ds.ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar' LIMIT 10
+}
+
+func ExampleUpdateDataset_LimitAll() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ LimitAll()
+ sql, _, _ := ds.ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar' LIMIT ALL
+}
+
+func ExampleUpdateDataset_ClearLimit() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Limit(10)
+ sql, _, _ := ds.ClearLimit().ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar'
+}
+
+func ExampleUpdateDataset_Order() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Order(goqu.C("a").Asc())
+ sql, _, _ := ds.ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar' ORDER BY `a` ASC
+}
+
+func ExampleUpdateDataset_OrderAppend() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Order(goqu.C("a").Asc())
+ sql, _, _ := ds.OrderAppend(goqu.C("b").Desc().NullsLast()).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar' ORDER BY `a` ASC, `b` DESC NULLS LAST
+}
+
+func ExampleUpdateDataset_OrderPrepend() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Order(goqu.C("a").Asc())
+
+ sql, _, _ := ds.OrderPrepend(goqu.C("b").Desc().NullsLast()).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar' ORDER BY `b` DESC NULLS LAST, `a` ASC
+}
+
+func ExampleUpdateDataset_ClearOrder() {
+ ds := goqu.Dialect("mysql").
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Order(goqu.C("a").Asc())
+ sql, _, _ := ds.ClearOrder().ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE `test` SET `foo`='bar'
+}
+
+func ExampleUpdateDataset_Where() {
+ // By default everything is anded together
+ sql, _, _ := goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql)
+ // You can use ExOr to get ORed expressions together
+ sql, _, _ = goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(goqu.ExOr{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql)
+ // You can use Or with Ex to Or multiple Ex maps together
+ sql, _, _ = goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.Or(
+ goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ },
+ goqu.Ex{
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ },
+ ),
+ ).ToSQL()
+ fmt.Println(sql)
+ // By default everything is anded together
+ sql, _, _ = goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.C("a").Gt(10),
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ goqu.C("d").In("a", "b", "c"),
+ ).ToSQL()
+ fmt.Println(sql)
+ // You can use a combination of Ors and Ands
+ sql, _, _ = goqu.Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+ ).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE "test" SET "foo"='bar' WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+ // UPDATE "test" SET "foo"='bar' WHERE (("a" > 10) OR ("b" < 10) OR ("c" IS NULL) OR ("d" IN ('a', 'b', 'c')))
+ // UPDATE "test" SET "foo"='bar' WHERE ((("a" > 10) AND ("b" < 10)) OR (("c" IS NULL) AND ("d" IN ('a', 'b', 'c'))))
+ // UPDATE "test" SET "foo"='bar' WHERE (("a" > 10) AND ("b" < 10) AND ("c" IS NULL) AND ("d" IN ('a', 'b', 'c')))
+ // UPDATE "test" SET "foo"='bar' WHERE (("a" > 10) OR (("b" < 10) AND ("c" IS NULL)))
+}
+
+func ExampleUpdateDataset_Where_prepared() {
+ // By default everything is anded together
+ sql, args, _ := goqu.Update("test").
+ Prepared(true).
+ Set(goqu.Record{"foo": "bar"}).
+ Where(goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql, args)
+ // You can use ExOr to get ORed expressions together
+ sql, args, _ = goqu.Update("test").Prepared(true).
+ Set(goqu.Record{"foo": "bar"}).
+ Where(goqu.ExOr{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ }).ToSQL()
+ fmt.Println(sql, args)
+ // You can use Or with Ex to Or multiple Ex maps together
+ sql, args, _ = goqu.Update("test").Prepared(true).
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.Or(
+ goqu.Ex{
+ "a": goqu.Op{"gt": 10},
+ "b": goqu.Op{"lt": 10},
+ },
+ goqu.Ex{
+ "c": nil,
+ "d": []string{"a", "b", "c"},
+ },
+ ),
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // By default everything is anded together
+ sql, args, _ = goqu.Update("test").Prepared(true).
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.C("a").Gt(10),
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ goqu.C("d").In("a", "b", "c"),
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // You can use a combination of Ors and Ands
+ sql, args, _ = goqu.Update("test").Prepared(true).
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // Output:
+ // UPDATE "test" SET "foo"=? WHERE (("a" > ?) AND ("b" < ?) AND ("c" IS NULL) AND ("d" IN (?, ?, ?))) [bar 10 10 a b c]
+ // UPDATE "test" SET "foo"=? WHERE (("a" > ?) OR ("b" < ?) OR ("c" IS NULL) OR ("d" IN (?, ?, ?))) [bar 10 10 a b c]
+ // UPDATE "test" SET "foo"=? WHERE ((("a" > ?) AND ("b" < ?)) OR (("c" IS NULL) AND ("d" IN (?, ?, ?)))) [bar 10 10 a b c]
+ // UPDATE "test" SET "foo"=? WHERE (("a" > ?) AND ("b" < ?) AND ("c" IS NULL) AND ("d" IN (?, ?, ?))) [bar 10 10 a b c]
+ // UPDATE "test" SET "foo"=? WHERE (("a" > ?) OR (("b" < ?) AND ("c" IS NULL))) [bar 10 10]
+}
+
+func ExampleUpdateDataset_ClearWhere() {
+ ds := goqu.
+ Update("test").
+ Set(goqu.Record{"foo": "bar"}).
+ Where(
+ goqu.Or(
+ goqu.C("a").Gt(10),
+ goqu.And(
+ goqu.C("b").Lt(10),
+ goqu.C("c").IsNull(),
+ ),
+ ),
+ )
+ sql, _, _ := ds.ClearWhere().ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE "test" SET "foo"='bar'
+}
+
+func ExampleUpdateDataset_Table() {
+ ds := goqu.Update("test")
+ sql, _, _ := ds.Table("test2").Set(goqu.Record{"foo": "bar"}).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE "test2" SET "foo"='bar'
+}
+
+func ExampleUpdateDataset_Table_aliased() {
+ ds := goqu.Update("test")
+ sql, _, _ := ds.Table(goqu.T("test").As("t")).Set(goqu.Record{"foo": "bar"}).ToSQL()
+ fmt.Println(sql)
+ // Output:
+ // UPDATE "test" AS "t" SET "foo"='bar'
+}
+
+func ExampleUpdateDataset_Set() {
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = goqu.Update("items").Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = goqu.Update("items").Set(
+ map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdateDataset_Set_struct() {
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdateDataset_Set_goquRecord() {
+ sql, args, _ := goqu.Update("items").Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdateDataset_Set_map() {
+ sql, args, _ := goqu.Update("items").Set(
+ map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdateDataset_Set_withSkipUpdateTag() {
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name" goqu:"skipupdate"`
+ }
+ sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr' []
+}
+
+func ExampleUpdateDataset_Set_withNoTags() {
+ type item struct {
+ Address string
+ Name string
+ }
+ sql, args, _ := goqu.Update("items").Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"='111 Test Addr',"name"='Test' []
+}
+
+func ExampleUpdateDataset_ToSQL_prepared() {
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+
+ sql, args, _ := goqu.From("items").Prepared(true).Update().Set(
+ item{Name: "Test", Address: "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = goqu.From("items").Prepared(true).Update().Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ sql, args, _ = goqu.From("items").Prepared(true).Update().Set(
+ map[string]interface{}{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+ // Output:
+ // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
+ // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
+ // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
+}
+
+func ExampleUpdateDataset_Prepared() {
+ sql, args, _ := goqu.Update("items").Prepared(true).Set(
+ goqu.Record{"name": "Test", "address": "111 Test Addr"},
+ ).ToSQL()
+ fmt.Println(sql, args)
+
+ // Output:
+ // UPDATE "items" SET "address"=?,"name"=? [111 Test Addr Test]
+}
diff --git a/update_dataset_test.go b/update_dataset_test.go
new file mode 100644
index 00000000..3072a772
--- /dev/null
+++ b/update_dataset_test.go
@@ -0,0 +1,889 @@
+package goqu
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "fmt"
+ "testing"
+ "time"
+
+ "github.com/DATA-DOG/go-sqlmock"
+ "github.com/doug-martin/goqu/v8/exec"
+ "github.com/doug-martin/goqu/v8/exp"
+ "github.com/doug-martin/goqu/v8/internal/errors"
+ "github.com/doug-martin/goqu/v8/internal/sb"
+ "github.com/doug-martin/goqu/v8/mocks"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+)
+
+type updateDatasetSuite struct {
+ suite.Suite
+}
+
+func (uds *updateDatasetSuite) SetupSuite() {
+ noReturn := DefaultDialectOptions()
+ noReturn.SupportsReturn = false
+ RegisterDialect("no-return", noReturn)
+
+ limitOnUpdate := DefaultDialectOptions()
+ limitOnUpdate.SupportsLimitOnUpdate = true
+ RegisterDialect("limit-on-update", limitOnUpdate)
+
+ orderOnUpdate := DefaultDialectOptions()
+ orderOnUpdate.SupportsOrderByOnUpdate = true
+ RegisterDialect("order-on-update", orderOnUpdate)
+}
+
+func (uds *updateDatasetSuite) TearDownSuite() {
+ DeregisterDialect("no-return")
+ DeregisterDialect("limit-on-update")
+ DeregisterDialect("order-on-update")
+}
+
+func (uds *updateDatasetSuite) TestClone() {
+ t := uds.T()
+ ds := Update("test")
+ assert.Equal(t, ds.Clone(), ds)
+}
+
+func (uds *updateDatasetSuite) TestExpression() {
+ t := uds.T()
+ ds := Update("test")
+ assert.Equal(t, ds.Expression(), ds)
+}
+
+func (uds *updateDatasetSuite) TestDialect() {
+ t := uds.T()
+ ds := Update("test")
+ assert.NotNil(t, ds.Dialect())
+}
+
+func (uds *updateDatasetSuite) TestWithDialect() {
+ t := uds.T()
+ ds := Update("test")
+ dialect := GetDialect("default")
+ ds.WithDialect("default")
+ assert.Equal(t, ds.Dialect(), dialect)
+}
+
+func (uds *updateDatasetSuite) TestPrepared() {
+ t := uds.T()
+ ds := Update("test")
+ preparedDs := ds.Prepared(true)
+ assert.True(t, preparedDs.IsPrepared())
+ assert.False(t, ds.IsPrepared())
+ // should apply the prepared to any datasets created from the root
+ assert.True(t, preparedDs.Where(Ex{"a": 1}).IsPrepared())
+}
+
+func (uds *updateDatasetSuite) TestGetClauses() {
+ t := uds.T()
+ ds := Update("test")
+ ce := exp.NewUpdateClauses().SetTable(I("test"))
+ assert.Equal(t, ce, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestWith() {
+ t := uds.T()
+ from := Update("cte")
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(false, "test-cte", from))
+ assert.Equal(t, ec, ds.With("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestWithRecursive() {
+ t := uds.T()
+ from := Update("cte")
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ ec := dsc.CommonTablesAppend(exp.NewCommonTableExpression(true, "test-cte", from))
+ assert.Equal(t, ec, ds.WithRecursive("test-cte", from).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestTable() {
+ t := uds.T()
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetTable(T("t"))
+ assert.Equal(t, ec, ds.Table(T("t")).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestTable_ToSQL() {
+ t := uds.T()
+ ds1 := Update("test").Set(C("a").Set("a1"))
+
+ updateSQL, _, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ ds2 := ds1.Table("test2")
+ updateSQL, _, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, updateSQL, `UPDATE "test2" SET "a"='a1'`)
+
+ // should not change original
+ updateSQL, _, err = ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithStructs() {
+ t := uds.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ ds1 := Update("items").Set(item{Name: "Test", Address: "111 Test Addr"})
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=?`, updateSQL)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithMaps() {
+ t := uds.T()
+ ds1 := Update("items").Set(Record{"name": "Test", "address": "111 Test Addr"})
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"111 Test Addr", "Test"})
+ assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=?`, updateSQL)
+
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithByteSlice() {
+ t := uds.T()
+ type item struct {
+ Name string `db:"name"`
+ Data []byte `db:"data"`
+ }
+ ds1 := Update("items").Set(item{Name: "Test", Data: []byte(`{"someJson":"data"}`)})
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "data"='{"someJson":"data"}',"name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, `UPDATE "items" SET "data"=?,"name"=?`, updateSQL)
+ assert.Equal(t, args, []interface{}{[]byte(`{"someJson":"data"}`), "Test"})
+}
+
+type valuerType []byte
+
+func (j valuerType) Value() (driver.Value, error) {
+ return []byte(fmt.Sprintf("%s World", string(j))), nil
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithCustomValuer() {
+ t := uds.T()
+ type item struct {
+ Name string `db:"name"`
+ Data valuerType `db:"data"`
+ }
+ ds1 := Update("items").Set(item{Name: "Test", Data: []byte(`Hello`)})
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "data"='Hello World',"name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{[]byte("Hello World"), "Test"})
+ assert.Equal(t, `UPDATE "items" SET "data"=?,"name"=?`, updateSQL)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithValuer() {
+ t := uds.T()
+ type item struct {
+ Name string `db:"name"`
+ Data sql.NullString `db:"data"`
+ }
+ ds1 := Update("items").Set(item{Name: "Test", Data: sql.NullString{String: "Hello World", Valid: true}})
+
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "data"='Hello World',"name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{"Hello World", "Test"})
+ assert.Equal(t, `UPDATE "items" SET "data"=?,"name"=?`, updateSQL)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithValuerNull() {
+ t := uds.T()
+ type item struct {
+ Name string `db:"name"`
+ Data sql.NullString `db:"data"`
+ }
+ ds1 := Update("items").Set(item{Name: "Test"})
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "data"=NULL,"name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"Test"}, args)
+ assert.Equal(t, `UPDATE "items" SET "data"=NULL,"name"=?`, updateSQL)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithEmbeddedStruct() {
+ t := uds.T()
+ type Phone struct {
+ Primary string `db:"primary_phone"`
+ Home string `db:"home_phone"`
+ Created time.Time `db:"phone_created"`
+ }
+ type item struct {
+ Phone
+ Address string `db:"address" goqu:"skipupdate"`
+ Name string `db:"name"`
+ Created time.Time `db:"created"`
+ NilPointer interface{} `db:"nil_pointer"`
+ }
+ created, _ := time.Parse("2006-01-02", "2015-01-01")
+ ds1 := Update("items").Set(item{
+ Name: "Test",
+ Address: "111 Test Addr",
+ Created: created,
+ Phone: Phone{
+ Home: "123123",
+ Primary: "456456",
+ Created: created,
+ },
+ })
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET `+
+ `"created"='2015-01-01T00:00:00Z',`+
+ `"home_phone"='123123',`+
+ `"name"='Test',`+
+ `"nil_pointer"=NULL,`+
+ `"phone_created"='2015-01-01T00:00:00Z',`+
+ `"primary_phone"='456456'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, `UPDATE "items" SET `+
+ `"created"=?,"home_phone"=?,"name"=?,"nil_pointer"=NULL,"phone_created"=?,"primary_phone"=?`, updateSQL)
+ assert.Equal(t, []interface{}{created, "123123", "Test", created, "456456"}, args)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithEmbeddedStructPtr() {
+ t := uds.T()
+ type Phone struct {
+ Primary string `db:"primary_phone"`
+ Home string `db:"home_phone"`
+ Created time.Time `db:"phone_created"`
+ }
+ type item struct {
+ *Phone
+ Address string `db:"address" goqu:"skipupdate"`
+ Name string `db:"name"`
+ Created time.Time `db:"created"`
+ }
+ created, _ := time.Parse("2006-01-02", "2015-01-01")
+
+ ds1 := Update("items").Set(item{
+ Name: "Test",
+ Address: "111 Test Addr",
+ Created: created,
+ Phone: &Phone{
+ Home: "123123",
+ Primary: "456456",
+ Created: created,
+ },
+ })
+
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, args, []interface{}{})
+ assert.Equal(t, `UPDATE "items" SET `+
+ `"created"='2015-01-01T00:00:00Z',`+
+ `"home_phone"='123123',`+
+ `"name"='Test',`+
+ `"phone_created"='2015-01-01T00:00:00Z',`+
+ `"primary_phone"='456456'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, `UPDATE "items" `+
+ `SET "created"=?,"home_phone"=?,"name"=?,"phone_created"=?,"primary_phone"=?`, updateSQL)
+ assert.Equal(t, []interface{}{created, "123123", "Test", created, "456456"}, args)
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithUnsupportedType() {
+ t := uds.T()
+ ds1 := Update("items").Set([]string{"HELLO"})
+
+ _, _, err := ds1.ToSQL()
+ assert.EqualError(t, err, "goqu: unsupported update interface type []string")
+
+ _, _, err = ds1.Prepared(true).ToSQL()
+ assert.EqualError(t, err, "goqu: unsupported update interface type []string")
+}
+
+func (uds *updateDatasetSuite) TestSet_ToSQLWithSkipupdateTag() {
+ t := uds.T()
+ type item struct {
+ Address string `db:"address" goqu:"skipupdate"`
+ Name string `db:"name"`
+ }
+ ds1 := Update("items").Set(item{Name: "Test", Address: "111 Test Addr"})
+
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "name"='Test'`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"Test"}, args)
+ assert.Equal(t, `UPDATE "items" SET "name"=?`, updateSQL)
+}
+
+func (uds *updateDatasetSuite) TestWhere() {
+ t := uds.T()
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ w := Ex{
+ "a": 1,
+ }
+ ec := dsc.WhereAppend(w)
+ assert.Equal(t, ec, ds.Where(w).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestWhere_ToSQL() {
+ t := uds.T()
+ ds1 := Update("test").Set(C("a").Set("a1"))
+
+ b := ds1.Where(
+ C("a").Eq(true),
+ C("a").Neq(true),
+ C("a").Eq(false),
+ C("a").Neq(false),
+ )
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' `+
+ `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? `+
+ `WHERE (("a" IS TRUE) AND ("a" IS NOT TRUE) AND ("a" IS FALSE) AND ("a" IS NOT FALSE))`)
+
+ b = ds1.Where(
+ C("a").Eq("a"),
+ C("b").Neq("b"),
+ C("c").Gt("c"),
+ C("d").Gte("d"),
+ C("e").Lt("e"),
+ C("f").Lte("f"),
+ )
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' `+
+ `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1", "a", "b", "c", "d", "e", "f"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? `+
+ `WHERE (("a" = ?) AND ("b" != ?) AND ("c" > ?) AND ("d" >= ?) AND ("e" < ?) AND ("f" <= ?))`)
+
+ b = ds1.Where(
+ C("a").Eq(From("test2").Select("id")),
+ )
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ b = ds1.Where(Ex{
+ "a": "a",
+ "b": Op{"neq": "b"},
+ "c": Op{"gt": "c"},
+ "d": Op{"gte": "d"},
+ "e": Op{"lt": "e"},
+ "f": Op{"lte": "f"},
+ })
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' `+
+ `WHERE (("a" = 'a') AND ("b" != 'b') AND ("c" > 'c') AND ("d" >= 'd') AND ("e" < 'e') AND ("f" <= 'f'))`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1", "a", "b", "c", "d", "e", "f"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? `+
+ `WHERE (("a" = ?) AND ("b" != ?) AND ("c" > ?) AND ("d" >= ?) AND ("e" < ?) AND ("f" <= ?))`)
+
+ b = ds1.Where(Ex{
+ "a": From("test2").Select("id"),
+ })
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? WHERE ("a" IN (SELECT "id" FROM "test2"))`)
+}
+
+func (uds *updateDatasetSuite) TestWhere_ToSQLEmpty() {
+ t := uds.T()
+ ds1 := Update("test").Set(C("a").Set("a1"))
+
+ b := ds1.Where()
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=?`)
+}
+
+func (uds *updateDatasetSuite) TestWhere_ToSQLWithChain() {
+ t := uds.T()
+ ds1 := Update("test").Set(C("a").Set("a1")).Where(
+ C("x").Eq(0),
+ C("y").Eq(1),
+ )
+
+ ds2 := ds1.Where(
+ C("z").Eq(2),
+ )
+
+ a := ds2.Where(
+ C("a").Eq("A"),
+ )
+ b := ds2.Where(
+ C("b").Eq("B"),
+ )
+ updateSQL, args, err := a.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' `+
+ `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("a" = 'A'))`)
+
+ updateSQL, args, err = a.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1", int64(0), int64(1), int64(2), "A"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? `+
+ `WHERE (("x" = ?) AND ("y" = ?) AND ("z" = ?) AND ("a" = ?))`)
+
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' `+
+ `WHERE (("x" = 0) AND ("y" = 1) AND ("z" = 2) AND ("b" = 'B'))`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1", int64(0), int64(1), int64(2), "B"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? `+
+ `WHERE (("x" = ?) AND ("y" = ?) AND ("z" = ?) AND ("b" = ?))`)
+}
+
+func (uds *updateDatasetSuite) TestClearWhere() {
+ t := uds.T()
+ w := Ex{
+ "a": 1,
+ }
+ ds := Update("test").Where(w)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearWhere()
+ assert.Equal(t, ec, ds.ClearWhere().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestClearWhere_ToSQL() {
+ t := uds.T()
+ ds1 := Update("test").Set(C("a").Set("a1"))
+
+ b := ds1.Where(
+ C("a").Eq(1),
+ ).ClearWhere()
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=?`)
+}
+
+func (uds *updateDatasetSuite) TestOrder() {
+ t := uds.T()
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ o := C("a").Desc()
+ ec := dsc.SetOrder(o)
+ assert.Equal(t, ec, ds.Order(o).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestOrder_ToSQL() {
+ t := uds.T()
+
+ ds1 := Update("test").WithDialect("order-on-update").Set(C("a").Set("a1"))
+
+ b := ds1.Order(C("a").Asc(), L(`("a" + "b" > 2)`).Asc())
+
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? ORDER BY "a" ASC, ("a" + "b" > 2) ASC`)
+}
+
+func (uds *updateDatasetSuite) TestOrderAppend() {
+ t := uds.T()
+ ds := Update("test").Order(C("a").Desc())
+ dsc := ds.GetClauses()
+ o := C("b").Desc()
+ ec := dsc.OrderAppend(o)
+ assert.Equal(t, ec, ds.OrderAppend(o).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestOrderAppend_ToSQL() {
+ t := uds.T()
+ ds := Update("test").WithDialect("order-on-update").Set(C("a").Set("a1"))
+
+ b := ds.Order(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ b = ds.OrderAppend(C("a").Asc().NullsFirst()).OrderAppend(C("b").Desc().NullsLast())
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? ORDER BY "a" ASC NULLS FIRST, "b" DESC NULLS LAST`)
+}
+
+func (uds *updateDatasetSuite) TestClearOrder() {
+ t := uds.T()
+ ds := Update("test").Order(C("a").Desc())
+ dsc := ds.GetClauses()
+ ec := dsc.ClearOrder()
+ assert.Equal(t, ec, ds.ClearOrder().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestClearOrder_ToSQL() {
+ t := uds.T()
+ b := Update("test").
+ WithDialect("order-on-update").
+ Set(C("a").Set("a1")).
+ Order(C("a").Asc().NullsFirst()).
+ ClearOrder()
+
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=?`)
+}
+
+func (uds *updateDatasetSuite) TestLimit() {
+ t := uds.T()
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLimit(uint(1))
+ assert.Equal(t, ec, ds.Limit(1).GetClauses())
+ assert.Equal(t, dsc, ds.Limit(0).GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestLimit_ToSQL() {
+ t := uds.T()
+ ds1 := Update("test").WithDialect("limit-on-update").Set(C("a").Set("a1"))
+
+ b := ds1.Limit(10)
+
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' LIMIT 10`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1", int64(10)}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? LIMIT ?`)
+
+ b = ds1.Limit(0)
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=?`)
+}
+
+func (uds *updateDatasetSuite) TestLimitAll() {
+ t := uds.T()
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetLimit(L("ALL"))
+ assert.Equal(t, ec, ds.LimitAll().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestLimitAll_ToSQL() {
+ t := uds.T()
+ ds1 := Update("test").WithDialect("limit-on-update").Set(C("a").Set("a1"))
+
+ b := ds1.LimitAll()
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' LIMIT ALL`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? LIMIT ALL`)
+
+ b = ds1.Limit(0).LimitAll()
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1' LIMIT ALL`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=? LIMIT ALL`)
+}
+
+func (uds *updateDatasetSuite) TestClearLimit() {
+ t := uds.T()
+ ds := Update("test").Limit(1)
+ dsc := ds.GetClauses()
+ ec := dsc.ClearLimit()
+ assert.Equal(t, ec, ds.ClearLimit().GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestClearLimit_ToSQL() {
+ t := uds.T()
+ ds1 := Update("test").WithDialect("limit-on-update").Set(C("a").Set("a1"))
+
+ b := ds1.LimitAll().ClearLimit()
+ updateSQL, args, err := b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=?`)
+
+ b = ds1.Limit(10).ClearLimit()
+ updateSQL, args, err = b.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"='a1'`)
+
+ updateSQL, args, err = b.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"a1"}, args)
+ assert.Equal(t, updateSQL, `UPDATE "test" SET "a"=?`)
+}
+
+func (uds *updateDatasetSuite) TestReturning() {
+ t := uds.T()
+ ds := Update("test")
+ dsc := ds.GetClauses()
+ ec := dsc.SetReturning(exp.NewColumnListExpression(C("a")))
+ assert.Equal(t, ec, ds.Returning("a").GetClauses())
+ assert.Equal(t, dsc, ds.GetClauses())
+}
+
+func (uds *updateDatasetSuite) TestReturning_ToSQL() {
+ t := uds.T()
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ ds := Update("items")
+ ds1 := ds.Set(item{Name: "Test", Address: "111 Test Addr"}).Returning(T("items").All())
+ ds2 := ds.Set(Record{"name": "Test", "address": "111 Test Addr"}).Returning(T("items").All())
+
+ updateSQL, args, err := ds1.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' RETURNING "items".*`, updateSQL)
+
+ updateSQL, args, err = ds1.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test"}, args)
+ assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? RETURNING "items".*`, updateSQL)
+
+ updateSQL, args, err = ds2.ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test' RETURNING "items".*`, updateSQL)
+
+ updateSQL, args, err = ds2.Prepared(true).ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test"}, args)
+ assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? RETURNING "items".*`, updateSQL)
+}
+
+func (uds *updateDatasetSuite) TestToSQL() {
+ t := uds.T()
+ md := new(mocks.SQLDialect)
+ ds := Update("test").SetDialect(md)
+ r := Record{"c": "a"}
+ c := ds.GetClauses().SetSetValues(r)
+ sqlB := sb.NewSQLBuilder(false)
+ md.On("ToUpdateSQL", sqlB, c).Return(nil).Once()
+ updateSQL, args, err := ds.Set(r).ToSQL()
+ assert.Empty(t, updateSQL)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (uds *updateDatasetSuite) TestToSQL_Prepared() {
+ t := uds.T()
+ md := new(mocks.SQLDialect)
+ ds := Update("test").Prepared(true).SetDialect(md)
+ r := Record{"c": "a"}
+ c := ds.GetClauses().SetSetValues(r)
+ sqlB := sb.NewSQLBuilder(true)
+ md.On("ToUpdateSQL", sqlB, c).Return(nil).Once()
+ updateSQL, args, err := ds.Set(Record{"c": "a"}).ToSQL()
+ assert.Empty(t, updateSQL)
+ assert.Empty(t, args)
+ assert.Nil(t, err)
+ md.AssertExpectations(t)
+}
+
+func (uds *updateDatasetSuite) TestToSQL_withNoSources() {
+ t := uds.T()
+ ds1 := newUpdateDataset("test", nil)
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ _, _, err := ds1.Set(item{Name: "Test", Address: "111 Test Addr"}).ToSQL()
+ assert.EqualError(t, err, "goqu: no source found when generating update sql")
+}
+
+func (uds *updateDatasetSuite) TestToSQL_withReturnNotSupported() {
+ t := uds.T()
+ ds1 := New("no-return", nil).Update("items")
+ type item struct {
+ Address string `db:"address"`
+ Name string `db:"name"`
+ }
+ _, _, err := ds1.Set(item{Name: "Test", Address: "111 Test Addr"}).Returning("id").ToSQL()
+ assert.EqualError(t, err, "goqu: adapter does not support RETURNING clause")
+}
+
+func (uds *updateDatasetSuite) TestToSQL_WithError() {
+ t := uds.T()
+ md := new(mocks.SQLDialect)
+ ds := Update("test").SetDialect(md)
+ r := Record{"c": "a"}
+ c := ds.GetClauses().SetSetValues(r)
+ sqlB := sb.NewSQLBuilder(false)
+ ee := errors.New("expected error")
+ md.On("ToUpdateSQL", sqlB, c).Run(func(args mock.Arguments) {
+ args.Get(0).(sb.SQLBuilder).SetError(ee)
+ }).Once()
+
+ updateSQL, args, err := ds.Set(Record{"c": "a"}).ToSQL()
+ assert.Empty(t, updateSQL)
+ assert.Empty(t, args)
+ assert.Equal(t, ee, err)
+ md.AssertExpectations(t)
+}
+
+func (uds *updateDatasetSuite) TestExecutor() {
+ t := uds.T()
+ mDb, _, err := sqlmock.New()
+ assert.NoError(t, err)
+ ds := newUpdateDataset("mock", exec.NewQueryFactory(mDb)).
+ Table("items").
+ Set(Record{"address": "111 Test Addr", "name": "Test1"}).
+ Where(C("name").IsNull())
+
+ updateSQL, args, err := ds.Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Empty(t, args)
+ assert.Equal(t, `UPDATE "items" SET "address"='111 Test Addr',"name"='Test1' WHERE ("name" IS NULL)`, updateSQL)
+
+ updateSQL, args, err = ds.Prepared(true).Executor().ToSQL()
+ assert.NoError(t, err)
+ assert.Equal(t, []interface{}{"111 Test Addr", "Test1"}, args)
+ assert.Equal(t, `UPDATE "items" SET "address"=?,"name"=? WHERE ("name" IS NULL)`, updateSQL)
+}
+
+func TestUpdateDataset(t *testing.T) {
+ suite.Run(t, new(updateDatasetSuite))
+}