Skip to content

Commit

Permalink
Enhancement for #119
Browse files Browse the repository at this point in the history
* [Added] Support for `DISTINCT ON` clauses #119
  • Loading branch information
doug-martin committed Aug 4, 2019
1 parent 2137511 commit 6ebed64
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 109 deletions.
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 8.3.0

* [Added] Support for `DISTINCT ON` clauses [#119](https://github.com/doug-martin/goqu/issues/119)

## v8.2.2

* [FIX] Scanner errors on pointers to primitive values [#122](https://github.com/doug-martin/goqu/issues/122)
Expand Down
1 change: 1 addition & 0 deletions dialect/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
opts.SupportsConflictTarget = false
opts.SupportsWithCTE = false
opts.SupportsWithCTERecursive = false
opts.SupportsDistinctOn = false

opts.UseFromClauseForMultipleUpdateTables = false

Expand Down
1 change: 1 addition & 0 deletions dialect/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
opts.SupportsConflictTarget = false
opts.SupportsMultipleUpdateTables = false
opts.WrapCompoundsInParens = false
opts.SupportsDistinctOn = false

opts.PlaceHolderRune = '?'
opts.IncludePlaceholderNum = false
Expand Down
44 changes: 40 additions & 4 deletions docs/selecting.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* [Creating a SelectDataset](#create)
* Building SQL
* [`Select`](#select)
* [`SelectDistinct`](#select_distinct)
* [`Distinct`](#distinct)
* [`From`](#from)
* [`Join`](#joins)
* [`Where`](#where)
Expand Down Expand Up @@ -162,11 +162,11 @@ Output:
SELECT "address", "email_address", "name" FROM "test"
```

<a name="select_distinct"></a>
**[`SelectDistinct`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.SelectDistinct)**
<a name="distinct"></a>
**[`Distinct`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.Distinct)**

```go
sql, _, _ := goqu.From("test").SelectDistinct("a", "b").ToSQL()
sql, _, _ := goqu.From("test").Select("a", "b").Distinct().ToSQL()
fmt.Println(sql)
```

Expand All @@ -175,6 +175,42 @@ Output:
SELECT DISTINCT "a", "b" FROM "test"
```

If you dialect supports `DISTINCT ON` you provide arguments to the `Distinct` method.

**NOTE** currently only the `postgres` and the default dialects support `DISTINCT ON` clauses

```go
sql, _, _ := goqu.From("test").Distinct("a").ToSQL()
fmt.Println(sql)
```
Output:

```
SELECT DISTINCT ON ("a") * FROM "test"
```

You can also provide other expression arguments

With `goqu.L`

```go
sql, _, _ := goqu.From("test").Distinct(goqu.L("COALESCE(?, ?)", goqu.C("a"), "empty")).ToSQL()
fmt.Println(sql)
```
Output:
```
SELECT DISTINCT ON (COALESCE("a", 'empty')) * FROM "test"
```
With `goqu.Coalesce`
```go
sql, _, _ := goqu.From("test").Distinct(goqu.COALESCE(goqu.C("a"), "empty")).ToSQL()
fmt.Println(sql)
```
Output:
```
SELECT DISTINCT ON (COALESCE("a", 'empty')) * FROM "test"
```

<a name="from"></a>
**[`From`](https://godoc.org/github.com/doug-martin/goqu/#SelectDataset.From)**

Expand Down
80 changes: 35 additions & 45 deletions exp/select_clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ type (
SelectAppend(cl ColumnListExpression) SelectClauses
SetSelect(cl ColumnListExpression) SelectClauses

SelectDistinct() ColumnListExpression
HasSelectDistinct() bool
SetSelectDistinct(cl ColumnListExpression) SelectClauses
Distinct() ColumnListExpression
SetDistinct(cle ColumnListExpression) SelectClauses

From() ColumnListExpression
SetFrom(cl ColumnListExpression) SelectClauses
Expand Down Expand Up @@ -61,20 +60,20 @@ type (
CommonTablesAppend(cte CommonTableExpression) SelectClauses
}
selectClauses struct {
commonTables []CommonTableExpression
selectColumns ColumnListExpression
selectDistinct ColumnListExpression
from ColumnListExpression
joins JoinExpressions
where ExpressionList
alias IdentifierExpression
groupBy ColumnListExpression
having ExpressionList
order ColumnListExpression
limit interface{}
offset uint
compounds []CompoundExpression
lock Lock
commonTables []CommonTableExpression
selectColumns ColumnListExpression
distinct ColumnListExpression
from ColumnListExpression
joins JoinExpressions
where ExpressionList
alias IdentifierExpression
groupBy ColumnListExpression
having ExpressionList
order ColumnListExpression
limit interface{}
offset uint
compounds []CompoundExpression
lock Lock
}
)

Expand Down Expand Up @@ -103,20 +102,20 @@ func (c *selectClauses) IsDefaultSelect() bool {

func (c *selectClauses) clone() *selectClauses {
return &selectClauses{
commonTables: c.commonTables,
selectColumns: c.selectColumns,
selectDistinct: c.selectDistinct,
from: c.from,
joins: c.joins,
where: c.where,
alias: c.alias,
groupBy: c.groupBy,
having: c.having,
order: c.order,
limit: c.limit,
offset: c.offset,
compounds: c.compounds,
lock: c.lock,
commonTables: c.commonTables,
selectColumns: c.selectColumns,
distinct: c.distinct,
from: c.from,
joins: c.joins,
where: c.where,
alias: c.alias,
groupBy: c.groupBy,
having: c.having,
order: c.order,
limit: c.limit,
offset: c.offset,
compounds: c.compounds,
lock: c.lock,
}
}

Expand All @@ -134,31 +133,22 @@ func (c *selectClauses) Select() ColumnListExpression {
}
func (c *selectClauses) SelectAppend(cl ColumnListExpression) SelectClauses {
ret := c.clone()
if ret.selectDistinct != nil {
ret.selectDistinct = ret.selectDistinct.Append(cl.Columns()...)
} else {
ret.selectColumns = ret.selectColumns.Append(cl.Columns()...)
}
ret.selectColumns = ret.selectColumns.Append(cl.Columns()...)
return ret
}

func (c *selectClauses) SetSelect(cl ColumnListExpression) SelectClauses {
ret := c.clone()
ret.selectDistinct = nil
ret.selectColumns = cl
return ret
}

func (c *selectClauses) SelectDistinct() ColumnListExpression {
return c.selectDistinct
}
func (c *selectClauses) HasSelectDistinct() bool {
return c.selectDistinct != nil
func (c *selectClauses) Distinct() ColumnListExpression {
return c.distinct
}
func (c *selectClauses) SetSelectDistinct(cl ColumnListExpression) SelectClauses {
func (c *selectClauses) SetDistinct(cle ColumnListExpression) SelectClauses {
ret := c.clone()
ret.selectColumns = nil
ret.selectDistinct = cl
ret.distinct = cle
return ret
}

Expand Down
24 changes: 9 additions & 15 deletions exp/select_clauses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,7 @@ func (sct *selectClausesTest) TestSelectAppend() {
c2 := c.SelectAppend(NewColumnListExpression("a"))

assert.Equal(t, NewColumnListExpression(Star()), c.Select())
assert.Nil(t, c.SelectDistinct())

assert.Equal(t, NewColumnListExpression(Star(), "a"), c2.Select())
assert.Nil(t, c2.SelectDistinct())
}

func (sct *selectClausesTest) TestSetSelect() {
Expand All @@ -77,35 +74,32 @@ func (sct *selectClausesTest) TestSetSelect() {
c2 := c.SetSelect(NewColumnListExpression("a"))

assert.Equal(t, NewColumnListExpression(Star()), c.Select())
assert.Nil(t, c.SelectDistinct())

assert.Equal(t, NewColumnListExpression("a"), c2.Select())
assert.Nil(t, c2.SelectDistinct())

}

func (sct *selectClausesTest) TestSelectDistinct() {
func (sct *selectClausesTest) TestDistinct() {
t := sct.T()
c := NewSelectClauses()
c2 := c.SetSelectDistinct(NewColumnListExpression("a"))
c2 := c.SetDistinct(NewColumnListExpression("a"))

assert.Nil(t, c.SelectDistinct())
assert.Nil(t, c.Distinct())
assert.Equal(t, NewColumnListExpression(Star()), c.Select())

assert.Equal(t, NewColumnListExpression("a"), c2.SelectDistinct())
assert.Nil(t, c2.Select())
assert.Equal(t, NewColumnListExpression("a"), c2.Distinct())
assert.Equal(t, NewColumnListExpression(Star()), c.Select())
}

func (sct *selectClausesTest) TestSetSelectDistinct() {
t := sct.T()
c := NewSelectClauses()
c2 := c.SetSelectDistinct(NewColumnListExpression("a"))
c2 := c.SetDistinct(NewColumnListExpression("a"))

assert.Nil(t, c.SelectDistinct())
assert.Nil(t, c.Distinct())
assert.Equal(t, NewColumnListExpression(Star()), c.Select())

assert.Equal(t, NewColumnListExpression("a"), c2.SelectDistinct())
assert.Nil(t, c2.Select())
assert.Equal(t, NewColumnListExpression("a"), c2.Distinct())
assert.Equal(t, NewColumnListExpression(Star()), c.Select())
}

func (sct *selectClausesTest) TestFrom() {
Expand Down
11 changes: 8 additions & 3 deletions select_dataset.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,14 +223,15 @@ func (sd *SelectDataset) Select(selects ...interface{}) *SelectDataset {
// 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
// Deprecated: Use Distinct() instead.
func (sd *SelectDataset) SelectDistinct(selects ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SetSelectDistinct(exp.NewColumnListExpression(selects...)))
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(selects...)).SetDistinct(exp.NewColumnListExpression()))
}

// Resets to SELECT *. If the SelectDistinct was used the returned Dataset will have the the dataset set to SELECT *.
// Resets to SELECT *. If the SelectDistinct or Distinct 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())))
return sd.copy(sd.clauses.SetSelect(exp.NewColumnListExpression(exp.Star())).SetDistinct(nil))
}

// Adds columns to the SELECT clause. See examples
Expand All @@ -244,6 +245,10 @@ func (sd *SelectDataset) SelectAppend(selects ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SelectAppend(exp.NewColumnListExpression(selects...)))
}

func (sd *SelectDataset) Distinct(on ...interface{}) *SelectDataset {
return sd.copy(sd.clauses.SetDistinct(exp.NewColumnListExpression(on...)))
}

// 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
Expand Down
29 changes: 25 additions & 4 deletions select_dataset_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,18 +939,39 @@ func ExampleSelectDataset_Select_withStruct() {
// SELECT "address", "email_address", "name" FROM "test"
}

func ExampleSelectDataset_SelectDistinct() {
sql, _, _ := goqu.From("test").SelectDistinct("a", "b").ToSQL()
func ExampleSelectDataset_Distinct() {
sql, _, _ := goqu.From("test").Select("a", "b").Distinct().ToSQL()
fmt.Println(sql)
// Output:
// SELECT DISTINCT "a", "b" FROM "test"
}

func ExampleSelectDataset_Distinct_on() {
sql, _, _ := goqu.From("test").Distinct("a").ToSQL()
fmt.Println(sql)
// Output:
// SELECT DISTINCT ON ("a") * FROM "test"
}

func ExampleSelectDataset_Distinct_onWithLiteral() {
sql, _, _ := goqu.From("test").Distinct(goqu.L("COALESCE(?, ?)", goqu.C("a"), "empty")).ToSQL()
fmt.Println(sql)
// Output:
// SELECT DISTINCT ON (COALESCE("a", 'empty')) * FROM "test"
}

func ExampleSelectDataset_Distinct_onCoalesce() {
sql, _, _ := goqu.From("test").Distinct(goqu.COALESCE(goqu.C("a"), "empty")).ToSQL()
fmt.Println(sql)
// Output:
// SELECT DISTINCT ON (COALESCE("a", 'empty')) * FROM "test"
}

func ExampleSelectDataset_SelectAppend() {
ds := goqu.From("test").Select("a", "b")
sql, _, _ := ds.SelectAppend("c").ToSQL()
fmt.Println(sql)
ds = goqu.From("test").SelectDistinct("a", "b")
ds = goqu.From("test").Select("a", "b").Distinct()
sql, _, _ = ds.SelectAppend("c").ToSQL()
fmt.Println(sql)
// Output:
Expand All @@ -962,7 +983,7 @@ func ExampleSelectDataset_ClearSelect() {
ds := goqu.From("test").Select("a", "b")
sql, _, _ := ds.ClearSelect().ToSQL()
fmt.Println(sql)
ds = goqu.From("test").SelectDistinct("a", "b")
ds = goqu.From("test").Select("a", "b").Distinct()
sql, _, _ = ds.ClearSelect().ToSQL()
fmt.Println(sql)
// Output:
Expand Down
Loading

0 comments on commit 6ebed64

Please sign in to comment.