Skip to content

Commit

Permalink
Merge pull request #188 from doug-martin/v9.5.1-rc
Browse files Browse the repository at this point in the history
V9.5.1 rc
  • Loading branch information
doug-martin authored Dec 7, 2019
2 parents fbe8322 + 17dbd79 commit 1dd9123
Show file tree
Hide file tree
Showing 22 changed files with 198 additions and 63 deletions.
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# v9.5.1

* [FIXED] WITH clause without a RETURNING clause will panic [#177](https://github.com/doug-martin/goqu/issues/177)
* [FIXED] SQlite dialect escapes single quotes wrong, leads to SQL syntax error [#178](https://github.com/doug-martin/goqu/issues/178)
* [FIXED] Fix ReturnsColumns() nil pointer panic [#181](https://github.com/doug-martin/goqu/issues/181) - [@yeaha](https://github.com/yeaha)
* [FIXED] SelectDataset From with Error [#183](https://github.com/doug-martin/goqu/issues/183)
* [FIXED] Unable to execute union with order by expression [#185](https://github.com/doug-martin/goqu/issues/185)

# v9.5.0

* [ADDED] Ability to use regexp like, ilike, notlike, and notilike without a regexp [#172](https://github.com/doug-martin/goqu/issues/172)
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
phony:

lint:
golangci-lint run
docker run --rm -v ${CURDIR}:/app -w /app golangci/golangci-lint:v1.21.0 golangci-lint run -v
6 changes: 5 additions & 1 deletion delete_dataset.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ func (dd *DeleteDataset) ToSQL() (sql string, params []interface{}, err error) {
// Appends this Dataset's DELETE statement to the SQLBuilder
// This is used internally when using deletes in CTEs
func (dd *DeleteDataset) AppendSQL(b sb.SQLBuilder) {
if dd.err != nil {
b.SetError(dd.err)
return
}
dd.dialect.ToDeleteSQL(b, dd.GetClauses())
}

Expand All @@ -217,7 +221,7 @@ func (dd *DeleteDataset) GetAs() exp.IdentifierExpression {
}

func (dd *DeleteDataset) ReturnsColumns() bool {
return !dd.clauses.Returning().IsEmpty()
return dd.clauses.HasReturning()
}

// Creates an QueryExecutor to execute the query.
Expand Down
6 changes: 6 additions & 0 deletions delete_dataset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ func (dds *deleteDatasetSuite) TestReturning() {
)
}

func (dds *deleteDatasetSuite) TestReturnsColumns() {
ds := Delete("test")
dds.False(ds.ReturnsColumns())
dds.True(ds.Returning("foo", "bar").ReturnsColumns())
}

func (dds *deleteDatasetSuite) TestToSQL() {
md := new(mocks.SQLDialect)
ds := Delete("test").SetDialect(md)
Expand Down
8 changes: 1 addition & 7 deletions dialect/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
}
opts.UseLiteralIsBools = false
opts.EscapedRunes = map[rune][]byte{
'\'': []byte("\\'"),
'"': []byte("\\\""),
'\\': []byte("\\\\"),
'\n': []byte("\\n"),
'\r': []byte("\\r"),
0: []byte("\\x00"),
0x1a: []byte("\\x1a"),
'\'': []byte("''"),
}
opts.InsertIgnoreClause = []byte("INSERT OR IGNORE")
opts.ConflictFragment = []byte("")
Expand Down
28 changes: 14 additions & 14 deletions dialect/sqlite3/sqlite3_dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,31 +63,31 @@ func (sds *sqlite3DialectSuite) TestLiteralString() {

sql, _, err = ds.Where(goqu.C("a").Eq("test'test")).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\'test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test''test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq(`test"test`)).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\\"test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\"test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq(`test\test`)).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\\\test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq("test\ntest")).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\ntest')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\ntest')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq("test\rtest")).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\rtest')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\rtest')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq("test\x00test")).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\x00test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\x00test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq("test\x1atest")).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\x1atest')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\x1atest')", sql)
}

func (sds *sqlite3DialectSuite) TestLiteralBytes() {
Expand All @@ -98,31 +98,31 @@ func (sds *sqlite3DialectSuite) TestLiteralBytes() {

sql, _, err = ds.Where(goqu.C("a").Eq([]byte("test'test"))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\'test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test''test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq([]byte(`test"test`))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\\"test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\"test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq([]byte(`test\test`))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\\\test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq([]byte("test\ntest"))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\ntest')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\ntest')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq([]byte("test\rtest"))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\rtest')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\rtest')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq([]byte("test\x00test"))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\x00test')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\x00test')", sql)

sql, _, err = ds.Where(goqu.C("a").Eq([]byte("test\x1atest"))).ToSQL()
sds.NoError(err)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\\x1atest')", sql)
sds.Equal("SELECT * FROM `test` WHERE (`a` = 'test\x1atest')", sql)
}

func (sds *sqlite3DialectSuite) TestBooleanOperations() {
Expand Down
14 changes: 12 additions & 2 deletions dialect/sqlite3/sqlite3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/dialect/mysql"
_ "github.com/mattn/go-sqlite3"

"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -360,25 +361,34 @@ func (st *sqlite3Suite) TestInsert() {
{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")},
{Int: 14, Float: 1.400000, String: `abc'd"e"f\\gh\n\ri\x00`, Time: now, Bool: true, Bytes: []byte("1.400000")},
}
_, err = ds.Insert().Rows(entries).Executor().Exec()
st.NoError(err)

var newEntries []entry
st.NoError(ds.Where(goqu.C("int").In([]uint32{11, 12, 13, 14})).ScanStructs(&newEntries))
st.Len(newEntries, 4)
for i, e := range newEntries {
st.Equal(entries[i].Int, e.Int)
st.Equal(entries[i].Float, e.Float)
st.Equal(entries[i].String, e.String)
st.Equal(entries[i].Time.UTC().Format(mysql.DialectOptions().TimeFormat), e.Time.Format(mysql.DialectOptions().TimeFormat))
st.Equal(entries[i].Bool, e.Bool)
st.Equal(entries[i].Bytes, e.Bytes)
}

_, 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")},
entry{Int: 18, Float: 1.800000, String: `abc'd"e"f\\gh\n\ri\x00`, Time: now, Bool: true, Bytes: []byte("1.800000")},
).Executor().Exec()
st.NoError(err)

newEntries = newEntries[0:0]
st.NoError(ds.Where(goqu.C("int").In([]uint32{15, 16, 17, 18})).ScanStructs(&newEntries))
st.Len(newEntries, 4)
st.Len(newEntries, 5)
}

func (st *sqlite3Suite) TestInsert_returning() {
Expand Down
2 changes: 1 addition & 1 deletion exp/delete_clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (dc *deleteClauses) Returning() ColumnListExpression {
}

func (dc *deleteClauses) HasReturning() bool {
return dc.returning != nil
return dc.returning != nil && !dc.returning.IsEmpty()
}

func (dc *deleteClauses) SetReturning(cl ColumnListExpression) DeleteClauses {
Expand Down
2 changes: 1 addition & 1 deletion exp/insert_clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (ic *insertClauses) Returning() ColumnListExpression {
}

func (ic *insertClauses) HasReturning() bool {
return ic.returning != nil
return ic.returning != nil && !ic.returning.IsEmpty()
}

func (ic *insertClauses) SetReturning(cl ColumnListExpression) InsertClauses {
Expand Down
2 changes: 1 addition & 1 deletion exp/update_clauses.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ func (uc *updateClauses) Returning() ColumnListExpression {
}

func (uc *updateClauses) HasReturning() bool {
return uc.returning != nil
return uc.returning != nil && !uc.returning.IsEmpty()
}

func (uc *updateClauses) SetReturning(cl ColumnListExpression) UpdateClauses {
Expand Down
6 changes: 5 additions & 1 deletion insert_dataset.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ func (id *InsertDataset) ToSQL() (sql string, params []interface{}, err error) {
// Appends this Dataset's INSERT statement to the SQLBuilder
// This is used internally when using inserts in CTEs
func (id *InsertDataset) AppendSQL(b sb.SQLBuilder) {
if id.err != nil {
b.SetError(id.err)
return
}
id.dialect.ToInsertSQL(b, id.GetClauses())
}

Expand All @@ -229,7 +233,7 @@ func (id *InsertDataset) GetAs() exp.IdentifierExpression {
}

func (id *InsertDataset) ReturnsColumns() bool {
return !id.clauses.Returning().IsEmpty()
return id.clauses.HasReturning()
}

// Generates the INSERT sql, and returns an QueryExecutor struct with the sql set to the INSERT statement
Expand Down
6 changes: 6 additions & 0 deletions insert_dataset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,12 @@ func (ids *insertDatasetSuite) TestReturning() {
)
}

func (ids *insertDatasetSuite) TestReturnsColumns() {
ds := Insert("test")
ids.False(ds.ReturnsColumns())
ids.True(ds.Returning("foo", "bar").ReturnsColumns())
}

func (ids *insertDatasetSuite) TestExecutor() {
mDb, _, err := sqlmock.New()
ids.NoError(err)
Expand Down
104 changes: 104 additions & 0 deletions issues_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package goqu_test

import (
"context"
"fmt"
"strings"
"sync"
"testing"
"time"

"github.com/DATA-DOG/go-sqlmock"
"github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/exp"
"github.com/stretchr/testify/suite"
)

Expand Down Expand Up @@ -324,6 +328,106 @@ func (gis *githubIssuesSuite) TestIssue164() {
)
}

// Test for https://github.com/doug-martin/goqu/issues/177
func (gis *githubIssuesSuite) TestIssue177() {
ds := goqu.Dialect("postgres").
From("ins1").
With("ins1",
goqu.Dialect("postgres").
Insert("account").
Rows(goqu.Record{"email": "email@email.com", "status": "active", "uuid": "XXX-XXX-XXXX"}).
Returning("*"),
).
With("ins2",
goqu.Dialect("postgres").
Insert("account_user").
Cols("account_id", "user_id").
FromQuery(goqu.Dialect("postgres").
From("ins1").
Select(
"id",
goqu.V(1001),
),
),
).
Select("*")
sql, args, err := ds.ToSQL()
gis.NoError(err)
gis.Equal(`WITH ins1 AS (`+
`INSERT INTO "account" ("email", "status", "uuid") VALUES ('email@email.com', 'active', 'XXX-XXX-XXXX') RETURNING *),`+
` ins2 AS (INSERT INTO "account_user" ("account_id", "user_id") SELECT "id", 1001 FROM "ins1")`+
` SELECT * FROM "ins1"`, sql)
gis.Len(args, 0)

sql, args, err = ds.Prepared(true).ToSQL()
gis.NoError(err)
gis.Equal(`WITH ins1 AS (INSERT INTO "account" ("email", "status", "uuid") VALUES ($1, $2, $3) RETURNING *), ins2`+
` AS (INSERT INTO "account_user" ("account_id", "user_id") SELECT "id", $4 FROM "ins1") SELECT * FROM "ins1"`, sql)
gis.Equal(args, []interface{}{"email@email.com", "active", "XXX-XXX-XXXX", int64(1001)})
}

// Test for https://github.com/doug-martin/goqu/issues/183
func (gis *githubIssuesSuite) TestIssue184() {
expectedErr := fmt.Errorf("an error")
testCases := []struct {
ds exp.AppendableExpression
}{
{ds: goqu.From("test").As("t").SetError(expectedErr)},
{ds: goqu.Insert("test").Rows(goqu.Record{"foo": "bar"}).Returning("foo").SetError(expectedErr)},
{ds: goqu.Update("test").Set(goqu.Record{"foo": "bar"}).Returning("foo").SetError(expectedErr)},
{ds: goqu.Update("test").Set(goqu.Record{"foo": "bar"}).Returning("foo").SetError(expectedErr)},
{ds: goqu.Delete("test").Returning("foo").SetError(expectedErr)},
}

for _, tc := range testCases {
ds := goqu.From(tc.ds)
sql, args, err := ds.ToSQL()
gis.Equal(expectedErr, err)
gis.Empty(sql)
gis.Empty(args)

sql, args, err = ds.Prepared(true).ToSQL()
gis.Equal(expectedErr, err)
gis.Empty(sql)
gis.Empty(args)

ds = goqu.From("test2").Where(goqu.Ex{"foo": tc.ds})

sql, args, err = ds.ToSQL()
gis.Equal(expectedErr, err)
gis.Empty(sql)
gis.Empty(args)

sql, args, err = ds.Prepared(true).ToSQL()
gis.Equal(expectedErr, err)
gis.Empty(sql)
gis.Empty(args)
}
}

// Test for https://github.com/doug-martin/goqu/issues/185
func (gis *githubIssuesSuite) TestIssue185() {
mDb, sqlMock, err := sqlmock.New()
gis.NoError(err)
sqlMock.ExpectQuery(
`SELECT \* FROM \(SELECT "id" FROM "table" ORDER BY "id" ASC\) AS "t1" UNION
\(SELECT \* FROM \(SELECT "id" FROM "table" ORDER BY "id" ASC\) AS "t1"\)`,
).
WillReturnRows(sqlmock.NewRows([]string{"id"}).FromCSVString("1\n2\n3\n4\n"))
db := goqu.New("mock", mDb)

ds := db.Select("id").From("table").Order(goqu.C("id").Asc()).
Union(
db.Select("id").From("table").Order(goqu.C("id").Asc()),
)

ctx := context.Background()
var i []int
gis.NoError(ds.ScanValsContext(ctx, &i))
gis.Equal([]int{1, 2, 3, 4}, i)

}

func TestGithubIssuesSuite(t *testing.T) {
suite.Run(t, new(githubIssuesSuite))
}
11 changes: 5 additions & 6 deletions select_dataset.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,7 @@ func (sd *SelectDataset) From(from ...interface{}) *SelectDataset {
// 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)

return sd.copy(exp.NewSelectClauses()).From(sd)
}

// Alias to InnerJoin. See examples.
Expand Down Expand Up @@ -554,6 +549,10 @@ func (sd *SelectDataset) Executor() exec.QueryExecutor {
// 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) {
if sd.err != nil {
b.SetError(sd.err)
return
}
sd.dialect.ToSelectSQL(b, sd.GetClauses())
}

Expand Down
Loading

0 comments on commit 1dd9123

Please sign in to comment.