Skip to content

Commit

Permalink
Merge pull request #96 from doug-martin/v7.2.0-rc
Browse files Browse the repository at this point in the history
V7.2.0 rc
  • Loading branch information
doug-martin authored Jul 12, 2019
2 parents d7ee44f + e75577a commit 3d12ff0
Show file tree
Hide file tree
Showing 18 changed files with 411 additions and 143 deletions.
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v7.2.0

* [FIXED] Sqlite3 does not accept SELECT * UNION (SELECT *) [#79](https://github.com/doug-martin/goqu/issues/79)
* [FIXED] Where(Ex{}) causes panics [mysql] [#49](https://github.com/doug-martin/goqu/issues/49)
* [ADDED] Support for OrderPrepend [#61](https://github.com/doug-martin/goqu/issues/61)
* [DOCS] Added new section about loading a dialect and using it to build SQL [#44](https://github.com/doug-martin/goqu/issues/44)

## v7.1.0

* [FIXED] Embedded pointers with property names that duplicate parent struct properties. [#23](https://github.com/doug-martin/goqu/issues/23)
Expand Down
139 changes: 121 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,128 @@ We tried a few other sql builders but each was a thin wrapper around sql fragmen

## Usage

* [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)
* [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)
* [Dialects](#dialects)
* [Custom Dialects](#custom-dialects)

<a name="dialect"></a>
## 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`

<a name="postgres"></a>
### 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")

// 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 []
```

<a name="mysql"></a>
### MySQL
```go
import (
"fmt"
"github.com/doug-martin/goqu/v7"
// import the dialect
_ "github.com/doug-martin/goqu/v7/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 []
```

<a name="sqlite3"></a>
### 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)
}
```

Output:
```
SELECT * FROM `test` WHERE `id` = 10 []
```

<a name="dataset"></a>
## Dataset
Expand Down Expand Up @@ -868,8 +970,8 @@ To enable trace logging of SQL statements use the [`Database.Logger`](http://god
**NOTE** If you start a transaction using a database your set a logger on the transaction will inherit that logger automatically


<a name="dialects"></a>
## Dialects
<a name="custom-dialects"></a>
## Custom Dialects

Dialects in goqu are the foundation of building the correct SQL for each DB dialect.

Expand Down Expand Up @@ -980,3 +1082,4 @@ GO_VERSION=latest docker-compose run goqu
`goqu` is released under the [MIT License](http://www.opensource.org/licenses/MIT).



8 changes: 7 additions & 1 deletion dataset.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ 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())
Expand Down Expand Up @@ -424,7 +430,7 @@ func (d *Dataset) IntersectAll(other *Dataset) *Dataset {
return d.withCompound(exp.IntersectAllCompoundType, other.CompoundFromSelf())
}

func (d *Dataset) withCompound(ct exp.CompoundType, other Expression) *Dataset {
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)
Expand Down
8 changes: 8 additions & 0 deletions dataset_sql_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,14 @@ func ExampleDataset_OrderAppend() {
// SELECT * FROM "test" ORDER BY "a" ASC, "b" DESC NULLS LAST
}

func ExampleDataset_OrderPrepend() {
ds := goqu.From("test").Order(goqu.C("a").Asc())
sql, _, _ := ds.OrderPrepend(goqu.C("b").Desc().NullsLast()).ToSQL()
fmt.Println(sql)
// Output:
// SELECT * FROM "test" ORDER BY "b" DESC NULLS LAST, "a" ASC
}

func ExampleDataset_ClearOrder() {
ds := goqu.From("test").Order(goqu.C("a").Asc())
sql, _, _ := ds.ClearOrder().ToSQL()
Expand Down
1 change: 1 addition & 0 deletions dialect/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
opts.SupportsConflictUpdateWhere = false
opts.SupportsInsertIgnoreSyntax = true
opts.SupportsConflictTarget = false
opts.WrapCompoundsInParens = false

opts.PlaceHolderRune = '?'
opts.IncludePlaceholderNum = false
Expand Down
17 changes: 17 additions & 0 deletions dialect/sqlite3/sqlite3_dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ func (sds *sqlite3DialectSuite) TestIdentifiers() {
assert.Equal(t, sql, "SELECT `a`, `a`.`b`.`c`, `c`.`d`, `test` AS `test` FROM `test`")
}

func (sds *sqlite3DialectSuite) TestCompoundExpressions() {
t := sds.T()
ds1 := sds.GetDs("test").Select("a")
ds2 := sds.GetDs("test2").Select("b")
sql, _, err := ds1.Union(ds2).ToSQL()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT `a` FROM `test` UNION SELECT `b` FROM `test2`")

sql, _, err = ds1.UnionAll(ds2).ToSQL()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT `a` FROM `test` UNION ALL SELECT `b` FROM `test2`")

sql, _, err = ds1.Intersect(ds2).ToSQL()
assert.NoError(t, err)
assert.Equal(t, sql, "SELECT `a` FROM `test` INTERSECT SELECT `b` FROM `test2`")
}

func (sds *sqlite3DialectSuite) TestLiteralString() {
t := sds.T()
ds := sds.GetDs("test")
Expand Down
21 changes: 21 additions & 0 deletions dialect/sqlite3/sqlite3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ func (st *sqlite3Suite) TestSelectSQL() {
assert.Equal(t, s, "SELECT * FROM `entry` WHERE `int` = ?")
}

func (st *sqlite3Suite) TestCompoundQueries() {
t := st.T()
ds1 := st.db.From("entry").Select("int").Where(goqu.C("int").Gt(0))
ds2 := st.db.From("entry").Select("int").Where(goqu.C("int").Gt(5))

var ids []int64
err := ds1.Union(ds2).ScanVals(&ids)
assert.NoError(t, err)
assert.Equal(t, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9}, ids)

ids = ids[0:0]
err = ds1.UnionAll(ds2).ScanVals(&ids)
assert.NoError(t, err)
assert.Equal(t, []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 7, 8, 9}, ids)

ids = ids[0:0]
err = ds1.Intersect(ds2).ScanVals(&ids)
assert.NoError(t, err)
assert.Equal(t, []int64{6, 7, 8, 9}, ids)
}

func (st *sqlite3Suite) TestQuery() {
t := st.T()
var entries []entry
Expand Down
10 changes: 10 additions & 0 deletions exp/clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type (
ClearOrder() Clauses
SetOrder(oes ...OrderedExpression) Clauses
OrderAppend(...OrderedExpression) Clauses
OrderPrepend(...OrderedExpression) Clauses

GroupBy() ColumnListExpression
SetGroupBy(cl ColumnListExpression) Clauses
Expand Down Expand Up @@ -285,6 +286,15 @@ func (c *clauses) OrderAppend(oes ...OrderedExpression) Clauses {
return ret
}

func (c *clauses) OrderPrepend(oes ...OrderedExpression) Clauses {
if c.order == nil {
return c.SetOrder(oes...)
}
ret := c.clone()
ret.order = NewOrderedColumnList(oes...).Append(ret.order.Columns()...)
return ret
}

func (c *clauses) GroupBy() ColumnListExpression {
return c.groupBy
}
Expand Down
21 changes: 17 additions & 4 deletions exp/clauses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,19 @@ func (ct *clausesTest) TestOrderAppend() {
assert.Equal(t, NewColumnListExpression(oe, oe2), c2.Order())
}

func (ct *clausesTest) TestOrderPrepend() {
t := ct.T()
oe := NewIdentifierExpression("", "", "a").Desc()
oe2 := NewIdentifierExpression("", "", "b").Desc()

c := NewClauses().SetOrder(oe)
c2 := c.OrderPrepend(oe2)

assert.Equal(t, NewColumnListExpression(oe), c.Order())

assert.Equal(t, NewColumnListExpression(oe2, oe), c2.Order())
}

func (ct *clausesTest) TestGroupBy() {
t := ct.T()
g := NewColumnListExpression(NewIdentifierExpression("", "", "a"))
Expand Down Expand Up @@ -458,7 +471,7 @@ func (ct *clausesTest) TestSetOffset() {
func (ct *clausesTest) TestCompounds() {
t := ct.T()

ce := NewCompoundExpression(UnionCompoundType, testSQLExpression("test_ce"))
ce := NewCompoundExpression(UnionCompoundType, newTestAppendableExpression("SELECT * FROM foo", []interface{}{}))

c := NewClauses()
c2 := c.CompoundsAppend(ce)
Expand All @@ -470,8 +483,8 @@ func (ct *clausesTest) TestCompounds() {
func (ct *clausesTest) TestCompoundsAppend() {
t := ct.T()

ce := NewCompoundExpression(UnionCompoundType, testSQLExpression("test_ce"))
ce2 := NewCompoundExpression(UnionCompoundType, testSQLExpression("test_ce_2"))
ce := NewCompoundExpression(UnionCompoundType, newTestAppendableExpression("SELECT * FROM foo1", []interface{}{}))
ce2 := NewCompoundExpression(UnionCompoundType, newTestAppendableExpression("SELECT * FROM foo2", []interface{}{}))

c := NewClauses().CompoundsAppend(ce)
c2 := c.CompoundsAppend(ce2)
Expand Down Expand Up @@ -511,7 +524,7 @@ func (ct *clausesTest) TestSetLock() {
func (ct *clausesTest) TestCommonTables() {
t := ct.T()

cte := NewCommonTableExpression(true, "test", testSQLExpression("test_cte"))
cte := NewCommonTableExpression(true, "test", newTestAppendableExpression(`SELECT * FROM "foo"`, []interface{}{}))

c := NewClauses()
c2 := c.CommonTablesAppend(cte)
Expand Down
10 changes: 5 additions & 5 deletions exp/compound.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package exp

type compound struct {
t CompoundType
rhs Expression
rhs AppendableExpression
}

func NewCompoundExpression(ct CompoundType, rhs Expression) CompoundExpression {
func NewCompoundExpression(ct CompoundType, rhs AppendableExpression) CompoundExpression {
return compound{t: ct, rhs: rhs}
}

func (c compound) Expression() Expression { return c }

func (c compound) Clone() Expression {
return compound{t: c.t, rhs: c.rhs.Clone().(SQLExpression)}
return compound{t: c.t, rhs: c.rhs.Clone().(AppendableExpression)}
}

func (c compound) Type() CompoundType { return c.t }
func (c compound) RHS() Expression { return c.rhs }
func (c compound) Type() CompoundType { return c.t }
func (c compound) RHS() AppendableExpression { return c.rhs }
4 changes: 3 additions & 1 deletion exp/exp.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ type (
CompoundExpression interface {
Expression
Type() CompoundType
RHS() Expression
RHS() AppendableExpression
}
// An Expression that the ON CONFLICT/ON DUPLICATE KEY portion of an INSERT statement
ConflictAction int
Expand Down Expand Up @@ -232,6 +232,8 @@ type (
Expressions() []Expression
// Returns a new expression list with the given expressions appended to the current Expressions list
Append(...Expression) ExpressionList

IsEmpty() bool
}
// An Identifier that can contain schema, table and column identifiers
IdentifierExpression interface {
Expand Down
Loading

0 comments on commit 3d12ff0

Please sign in to comment.