Skip to content

Commit

Permalink
v8.5.0
Browse files Browse the repository at this point in the history
* [ADDED] Window Function support #128 - @Xuyuanp
  • Loading branch information
doug-martin committed Aug 25, 2019
1 parent 557ec17 commit 939c031
Show file tree
Hide file tree
Showing 26 changed files with 1,046 additions and 292 deletions.
15 changes: 13 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,31 @@ on:
pull_request: # Specify a second event with pattern matching
jobs:
test:
name: Test go ${{ matrix.go_version }}
name: Test go - ${{ matrix.go_version }} mysql - ${{ matrix.db_versions.mysql_version}} postgres - ${{ matrix.db_versions.postgres_version}}
runs-on: ubuntu-latest
strategy:
matrix:
go_version: ["1.10", "1.11", "latest"]
db_versions:
- mysql_version: 5
postgres_version: 9.6
- mysql_version: 5
postgres_version: "10.10"
- mysql_version: 8
postgres_version: 11.5
steps:
- name: checkout
uses: actions/checkout@v1
- name: Test
env:
GO_VERSION: ${{ matrix.go_version }}
MYSQL_VERSION: ${{ matrix.db_versions.mysql_version }}
POSTGRES_VERSION: ${{ matrix.db_versions.postgres_version }}
run: docker-compose run goqu-coverage
- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash) -n $GO_VERSION -e GO_VERSION,GITHUB_WORKFLOW,GITHUB_ACTION
run: bash <(curl -s https://codecov.io/bash) -n $GO_VERSION -e GO_VERSION,MYSQL_VERSION,POSTGRES_VERSION,GITHUB_WORKFLOW,GITHUB_ACTION
env:
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
GO_VERSION: ${{ matrix.go_version }}
MYSQL_VERSION: ${{ matrix.db_versions.mysql_version }}
POSTGRES_VERSION: ${{ matrix.db_versions.postgres_version }}
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 8.5.0

* [ADDED] Window Function support [#128](https://github.com/doug-martin/goqu/issues/128) - [@Xuyuanp](https://github.com/Xuyuanp)

## 8.4.1

* [FIXED] Returning func be able to handle nil [#140](https://github.com/doug-martin/goqu/issues/140)
Expand Down
28 changes: 12 additions & 16 deletions database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"sync"
"testing"

"github.com/stretchr/testify/assert"

"github.com/DATA-DOG/go-sqlmock"
"github.com/doug-martin/goqu/v8/internal/errors"
"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -317,9 +315,8 @@ func (ds *databaseSuite) TestWithTx() {
}

func (ds *databaseSuite) TestDataRace() {
t := ds.T()
mDb, mock, err := sqlmock.New()
assert.NoError(t, err)
ds.NoError(err)
db := newDatabase("mock", mDb)

const concurrency = 10
Expand All @@ -340,10 +337,10 @@ func (ds *databaseSuite) TestDataRace() {
sql := db.From("items").Limit(1)
var item testActionItem
found, err := sql.ScanStruct(&item)
assert.NoError(t, err)
assert.True(t, found)
assert.Equal(t, item.Address, "111 Test Addr")
assert.Equal(t, item.Name, "Test1")
ds.NoError(err)
ds.True(found)
ds.Equal(item.Address, "111 Test Addr")
ds.Equal(item.Name, "Test1")
}()
}

Expand Down Expand Up @@ -661,13 +658,12 @@ func (tds *txdatabaseSuite) TestWrap() {
}

func (tds *txdatabaseSuite) TestDataRace() {
t := tds.T()
mDb, mock, err := sqlmock.New()
assert.NoError(t, err)
tds.NoError(err)
mock.ExpectBegin()
db := newDatabase("mock", mDb)
tx, err := db.Begin()
assert.NoError(t, err)
tds.NoError(err)

const concurrency = 10

Expand All @@ -687,16 +683,16 @@ func (tds *txdatabaseSuite) TestDataRace() {
sql := tx.From("items").Limit(1)
var item testActionItem
found, err := sql.ScanStruct(&item)
assert.NoError(t, err)
assert.True(t, found)
assert.Equal(t, item.Address, "111 Test Addr")
assert.Equal(t, item.Name, "Test1")
tds.NoError(err)
tds.True(found)
tds.Equal(item.Address, "111 Test Addr")
tds.Equal(item.Name, "Test1")
}()
}

wg.Wait()
mock.ExpectCommit()
assert.NoError(t, tx.Commit())
tds.NoError(tx.Commit())
}

func TestTxDatabaseSuite(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion dialect/mysql/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,13 @@ func (mt *mysqlTest) TestWindowFunction() {
major, err := strconv.Atoi(fields[0])
mt.NoError(err)
if major < 8 {
fmt.Printf("SKIPPING MYSQL WINDOW FUNCTION TEST BECAUSE VERSION IS < 8 [mysql_version:=%d]\n", major)
return
}

ds := mt.db.From("entry").Select("int", goqu.ROW_NUMBER().OverName("w").As("id")).Windows(goqu.W("w").OrderBy(goqu.I("int").Desc()))
ds := mt.db.From("entry").
Select("int", goqu.ROW_NUMBER().OverName(goqu.I("w")).As("id")).
Window(goqu.W("w").OrderBy(goqu.I("int").Desc()))

var entries []entry
mt.NoError(ds.WithDialect("mysql8").ScanStructs(&entries))
Expand Down
22 changes: 22 additions & 0 deletions dialect/postgres/postgres_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,28 @@ func (pt *postgresTest) TestInsert_OnConflict() {
pt.Equal("upsert", entry9.String)
}

func (pt *postgresTest) TestWindowFunction() {
ds := pt.db.From("entry").
Select("int", goqu.ROW_NUMBER().OverName(goqu.I("w")).As("id")).
Window(goqu.W("w").OrderBy(goqu.I("int").Desc()))

var entries []entry
pt.NoError(ds.ScanStructs(&entries))

pt.Equal([]entry{
{Int: 9, ID: 1},
{Int: 8, ID: 2},
{Int: 7, ID: 3},
{Int: 6, ID: 4},
{Int: 5, ID: 5},
{Int: 4, ID: 6},
{Int: 3, ID: 7},
{Int: 2, ID: 8},
{Int: 1, ID: 9},
{Int: 0, ID: 10},
}, entries)
}

func TestPostgresSuite(t *testing.T) {
suite.Run(t, new(postgresTest))
}
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ version: "2"

services:
postgres:
image: postgres:9.6
image: "postgres:${POSTGRES_VERSION}"
environment:
- "POSTGRES_USER=postgres"
- "POSTGRES_DB=goqupostgres"
expose:
- "5432"

mysql:
image: mysql:5
image: "mysql:${MYSQL_VERSION}"
environment:
- "MYSQL_DATABASE=goqumysql"
- "MYSQL_ALLOW_EMPTY_PASSWORD=yes"
Expand Down
28 changes: 22 additions & 6 deletions docs/selecting.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,25 +613,40 @@ SELECT * FROM "test" GROUP BY "age" HAVING (SUM("income") > 1000)


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

**NOTE** currently only the `postgres`, `mysql8` (NOT `mysql`) and the default dialect support `Window Function`

To use windowing in select you can use the `Over` method on an `SQLFunction`

```go
sql, _, _ = goqu.From("test").Select(goqu.ROW_NUMBER().Over(goqu.W().PartitionBy("a").OrderBy("b")))
sql, _, _ := goqu.From("test").Select(
goqu.ROW_NUMBER().Over(goqu.W().PartitionBy("a").OrderBy(goqu.I("b").Asc())),
)
fmt.Println(sql)
```

Output:

```
SELECT ROW_NUMBER() OVER (PARTITION BY "a" ORDER BY "b") FROM "test"
```

sql, _, _ = goqu.From("test").Select(goqu.ROW_NUMBER().OverName("w")).Windows(goqu.W("w").PartitionBy("a").OrderBy("b"))
`goqu` also supports the `WINDOW` clause.

```go
sql, _, _ := goqu.From("test").
Select(goqu.ROW_NUMBER().OverName(goqu.I("w"))).
Window(goqu.W("w").PartitionBy("a").OrderBy(goqu.I("b").Asc()))
fmt.Println(sql)
```

Output:

```
SELECT ROW_NUMBER() OVER (PARTITION BY "a" ORDER BY "b") FROM "test"
SELECT ROW_NUMBER() OVER "w" FROM "test" WINDOW "w" AS (PARTITION BY "a" ORDER BY "b")
```

**NOTE** currently only the `postgres`, `mysql8`(NOT `mysql`) and the default dialect support `Window Function`

## Executing Queries

To execute your query use [`goqu.Database#From`](https://godoc.org/github.com/doug-martin/goqu/#Database.From) to create your dataset
Expand Down Expand Up @@ -771,3 +786,4 @@ if err := db.From("user").Pluck(&ids, "id"); err != nil{
}
fmt.Printf("\nIds := %+v", ids)
```

28 changes: 21 additions & 7 deletions exp/exp.go
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ type (
End() interface{}
}

Windowable interface {
Over(WindowExpression) SQLWindowFunctionExpression
OverName(IdentifierExpression) SQLWindowFunctionExpression
}

// Expression for representing a SQLFunction(e.g. COUNT, SUM, MIN, MAX...)
SQLFunctionExpression interface {
Expression
Expand All @@ -350,6 +355,7 @@ type (
Isable
Inable
Likeable
Windowable
// The function name
Name() string
// Arguments to be passed to the function
Expand All @@ -362,13 +368,17 @@ type (
}

SQLWindowFunctionExpression interface {
SQLFunctionExpression
Expression
Aliaseable
Rangeable
Comparable
Isable
Inable
Likeable
Func() SQLFunctionExpression

Window() WindowExpression
WindowName() string

Over(WindowExpression) SQLWindowFunctionExpression
OverName(string) SQLWindowFunctionExpression
WindowName() IdentifierExpression

HasWindow() bool
HasWindowName() bool
Expand All @@ -377,11 +387,15 @@ type (
WindowExpression interface {
Expression

Name() string
Name() IdentifierExpression
HasName() bool

Parent() string
Parent() IdentifierExpression
HasParent() bool
PartitionCols() ColumnListExpression
HasPartitionBy() bool
OrderCols() ColumnListExpression
HasOrder() bool

Inherit(parent string) WindowExpression
PartitionBy(cols ...interface{}) WindowExpression
Expand Down
8 changes: 8 additions & 0 deletions exp/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,11 @@ func (sfe sqlFunctionExpression) IsTrue() BooleanExpression { retu
func (sfe sqlFunctionExpression) IsNotTrue() BooleanExpression { return isNot(sfe, true) }
func (sfe sqlFunctionExpression) IsFalse() BooleanExpression { return is(sfe, false) }
func (sfe sqlFunctionExpression) IsNotFalse() BooleanExpression { return isNot(sfe, false) }

func (sfe sqlFunctionExpression) Over(we WindowExpression) SQLWindowFunctionExpression {
return NewSQLWindowFunctionExpression(sfe, nil, we)
}

func (sfe sqlFunctionExpression) OverName(windowName IdentifierExpression) SQLWindowFunctionExpression {
return NewSQLWindowFunctionExpression(sfe, windowName, nil)
}
4 changes: 2 additions & 2 deletions exp/select_clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type (

Windows() []WindowExpression
SetWindows(ws []WindowExpression) SelectClauses
WindowsAppend(ws []WindowExpression) SelectClauses
WindowsAppend(ws ...WindowExpression) SelectClauses
ClearWindows() SelectClauses
}
selectClauses struct {
Expand Down Expand Up @@ -349,7 +349,7 @@ func (c *selectClauses) SetWindows(ws []WindowExpression) SelectClauses {
return ret
}

func (c *selectClauses) WindowsAppend(ws []WindowExpression) SelectClauses {
func (c *selectClauses) WindowsAppend(ws ...WindowExpression) SelectClauses {
ret := c.clone()
ret.windows = append(ret.windows, ws...)
return ret
Expand Down
28 changes: 11 additions & 17 deletions exp/select_clauses_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,19 +255,19 @@ func (scs *selectClausesSuite) TestHavingAppend() {
scs.Equal(NewExpressionList(AndType, w, w2), c4.Having())
}

func (scs *selectClausesSuite) TestWindow() {
w := NewWindowExpression("w", "", nil, nil)
func (scs *selectClausesSuite) TestWindows() {
w := NewWindowExpression(NewIdentifierExpression("", "", "w"), nil, nil, nil)

c := NewSelectClauses()
c2 := c.WindowsAppend([]WindowExpression{w})
c2 := c.WindowsAppend(w)

scs.Nil(c.Windows())

scs.Equal([]WindowExpression{w}, c2.Windows())
}

func (scs *selectClausesSuite) TestSetWindows() {
w := NewWindowExpression("w", "", nil, nil)
w := NewWindowExpression(NewIdentifierExpression("", "", "w"), nil, nil, nil)

c := NewSelectClauses()
c2 := c.SetWindows([]WindowExpression{w})
Expand All @@ -278,29 +278,23 @@ func (scs *selectClausesSuite) TestSetWindows() {
}

func (scs *selectClausesSuite) TestWindowsAppend() {
w1 := NewWindowExpression("w1", "", nil, nil)
w2 := NewWindowExpression("w2", "", nil, nil)
w1 := NewWindowExpression(NewIdentifierExpression("", "", "w1"), nil, nil, nil)
w2 := NewWindowExpression(NewIdentifierExpression("", "", "w2"), nil, nil, nil)

c := NewSelectClauses()
c2 := c.WindowsAppend([]WindowExpression{w1}).WindowsAppend([]WindowExpression{w2})
c2 := c.WindowsAppend(w1).WindowsAppend(w2)

scs.Nil(c.Windows())

scs.Equal([]WindowExpression{w1, w2}, c2.Windows())
}

func (scs *selectClausesSuite) TestClearWindows() {
w := NewWindowExpression("w", "", nil, nil)
w := NewWindowExpression(NewIdentifierExpression("", "", "w"), nil, nil, nil)

c := NewSelectClauses()
c2 := c.SetWindows([]WindowExpression{w})

scs.Nil(c.Windows())

scs.Equal([]WindowExpression{w}, c2.Windows())

c3 := c.ClearWindows()
scs.Nil(c3.Windows())
c := NewSelectClauses().SetWindows([]WindowExpression{w})
scs.Nil(c.ClearWindows().Windows())
scs.Equal([]WindowExpression{w}, c.Windows())
}

func (scs *selectClausesSuite) TestOrder() {
Expand Down
Loading

0 comments on commit 939c031

Please sign in to comment.