Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync from origin #1

Merged
merged 5 commits into from
Oct 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v9.17.0
* [FEATURE] Add support bitwise operations [#303](https://github.com/doug-martin/goqu/pull/303) - [@XIELongDragon](https://github.com/XIELongDragon)
* [FEATURE] Add support for specifying tables to be locked in ForUpdate, ForNoKeyUpdate, ForKeyShare, ForShare [#299](https://github.com/doug-martin/goqu/pull/299) - [@jbub](https://github.com/jbub)

# v9.16.0
* [FEATURE] Allow ordering by case expression [#282](https://github.com/doug-martin/goqu/issues/282), [#292](https://github.com/doug-martin/goqu/pull/292)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ New features and/or enhancements are great and I encourage you to either submit
1. The use case
2. A short example

If you are issuing a PR also also include the following
If you are issuing a PR also include the following

1. Tests - otherwise the PR will not be merged
2. Documentation - otherwise the PR will not be merged
Expand All @@ -297,7 +297,7 @@ go test -v -race ./...
You can also run the tests in a container using [docker-compose](https://docs.docker.com/compose/).

```sh
GO_VERSION=latest docker-compose run goqu
MYSQL_VERSION=8 POSTGRES_VERSION=13.4 SQLSERVER_VERSION=2017-CU8-ubuntu GO_VERSION=latest docker-compose run goqu
```

## License
Expand Down
8 changes: 8 additions & 0 deletions dialect/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ func DialectOptions() *goqu.SQLDialectOptions {
exp.RegexpILikeOp: []byte("REGEXP"),
exp.RegexpNotILikeOp: []byte("NOT REGEXP"),
}
opts.BitwiseOperatorLookup = map[exp.BitwiseOperation][]byte{
exp.BitwiseInversionOp: []byte("~"),
exp.BitwiseOrOp: []byte("|"),
exp.BitwiseAndOp: []byte("&"),
exp.BitwiseXorOp: []byte("^"),
exp.BitwiseLeftShiftOp: []byte("<<"),
exp.BitwiseRightShiftOp: []byte(">>"),
}
opts.EscapedRunes = map[rune][]byte{
'\'': []byte("\\'"),
'"': []byte("\\\""),
Expand Down
13 changes: 13 additions & 0 deletions dialect/mysql/mysql_dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ func (mds *mysqlDialectSuite) TestBooleanOperations() {
)
}

func (mds *mysqlDialectSuite) TestBitwiseOperations() {
col := goqu.C("a")
ds := mds.GetDs("test")
mds.assertSQL(
sqlTestCase{ds: ds.Where(col.BitwiseInversion()), sql: "SELECT * FROM `test` WHERE (~ `a`)"},
sqlTestCase{ds: ds.Where(col.BitwiseAnd(1)), sql: "SELECT * FROM `test` WHERE (`a` & 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseOr(1)), sql: "SELECT * FROM `test` WHERE (`a` | 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseXor(1)), sql: "SELECT * FROM `test` WHERE (`a` ^ 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseLeftShift(1)), sql: "SELECT * FROM `test` WHERE (`a` << 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseRightShift(1)), sql: "SELECT * FROM `test` WHERE (`a` >> 1)"},
)
}

func (mds *mysqlDialectSuite) TestUpdateSQL() {
ds := mds.GetDs("test").Update()
mds.assertSQL(
Expand Down
7 changes: 7 additions & 0 deletions dialect/sqlite3/sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ func DialectOptions() *goqu.SQLDialectOptions {
exp.RegexpNotILikeOp: []byte("NOT REGEXP"),
}
opts.UseLiteralIsBools = false
opts.BitwiseOperatorLookup = map[exp.BitwiseOperation][]byte{
exp.BitwiseOrOp: []byte("|"),
exp.BitwiseAndOp: []byte("&"),
exp.BitwiseLeftShiftOp: []byte("<<"),
exp.BitwiseRightShiftOp: []byte(">>"),
}
opts.EscapedRunes = map[rune][]byte{
'\'': []byte("''"),
}
Expand All @@ -60,6 +66,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
opts.ConflictDoUpdateFragment = []byte(" DO UPDATE SET ")
opts.ConflictDoNothingFragment = []byte(" DO NOTHING ")
opts.ForUpdateFragment = []byte("")
opts.OfFragment = []byte("")
opts.NowaitFragment = []byte("")
return opts
}
Expand Down
13 changes: 13 additions & 0 deletions dialect/sqlite3/sqlite3_dialect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@ func (sds *sqlite3DialectSuite) TestBooleanOperations() {
)
}

func (sds *sqlite3DialectSuite) TestBitwiseOperations() {
col := goqu.C("a")
ds := sds.GetDs("test")
sds.assertSQL(
sqlTestCase{ds: ds.Where(col.BitwiseInversion()), err: "goqu: bitwise operator 'Inversion' not supported"},
sqlTestCase{ds: ds.Where(col.BitwiseAnd(1)), sql: "SELECT * FROM `test` WHERE (`a` & 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseOr(1)), sql: "SELECT * FROM `test` WHERE (`a` | 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseXor(1)), err: "goqu: bitwise operator 'XOR' not supported"},
sqlTestCase{ds: ds.Where(col.BitwiseLeftShift(1)), sql: "SELECT * FROM `test` WHERE (`a` << 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseRightShift(1)), sql: "SELECT * FROM `test` WHERE (`a` >> 1)"},
)
}

func (sds *sqlite3DialectSuite) TestForUpdate() {
ds := sds.GetDs("test")
sds.assertSQL(
Expand Down
7 changes: 7 additions & 0 deletions dialect/sqlserver/sqlserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ func DialectOptions() *goqu.SQLDialectOptions {
exp.RegexpILikeOp: []byte("REGEXP"),
exp.RegexpNotILikeOp: []byte("NOT REGEXP"),
}
opts.BitwiseOperatorLookup = map[exp.BitwiseOperation][]byte{
exp.BitwiseInversionOp: []byte("~"),
exp.BitwiseOrOp: []byte("|"),
exp.BitwiseAndOp: []byte("&"),
exp.BitwiseXorOp: []byte("^"),
}

opts.FetchFragment = []byte(" FETCH FIRST ")

Expand Down Expand Up @@ -80,6 +86,7 @@ func DialectOptions() *goqu.SQLDialectOptions {
0x1a: []byte("\\x1a"),
}

opts.OfFragment = []byte("")
opts.ConflictFragment = []byte("")
opts.ConflictDoUpdateFragment = []byte("")
opts.ConflictDoNothingFragment = []byte("")
Expand Down
60 changes: 60 additions & 0 deletions dialect/sqlserver/sqlserver_dialect_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package sqlserver_test

import (
"testing"

"github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/exp"
"github.com/stretchr/testify/suite"
)

type (
sqlserverDialectSuite struct {
suite.Suite
}
sqlTestCase struct {
ds exp.SQLExpression
sql string
err string
isPrepared bool
args []interface{}
}
)

func (sds *sqlserverDialectSuite) GetDs(table string) *goqu.SelectDataset {
return goqu.Dialect("sqlserver").From(table)
}

func (sds *sqlserverDialectSuite) assertSQL(cases ...sqlTestCase) {
for i, c := range cases {
actualSQL, actualArgs, err := c.ds.ToSQL()
if c.err == "" {
sds.NoError(err, "test case %d failed", i)
} else {
sds.EqualError(err, c.err, "test case %d failed", i)
}
sds.Equal(c.sql, actualSQL, "test case %d failed", i)
if c.isPrepared && c.args != nil || len(c.args) > 0 {
sds.Equal(c.args, actualArgs, "test case %d failed", i)
} else {
sds.Empty(actualArgs, "test case %d failed", i)
}
}
}

func (sds *sqlserverDialectSuite) TestBitwiseOperations() {
col := goqu.C("a")
ds := sds.GetDs("test")
sds.assertSQL(
sqlTestCase{ds: ds.Where(col.BitwiseInversion()), sql: "SELECT * FROM \"test\" WHERE (~ \"a\")"},
sqlTestCase{ds: ds.Where(col.BitwiseAnd(1)), sql: "SELECT * FROM \"test\" WHERE (\"a\" & 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseOr(1)), sql: "SELECT * FROM \"test\" WHERE (\"a\" | 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseXor(1)), sql: "SELECT * FROM \"test\" WHERE (\"a\" ^ 1)"},
sqlTestCase{ds: ds.Where(col.BitwiseLeftShift(1)), err: "goqu: bitwise operator 'Left Shift' not supported"},
sqlTestCase{ds: ds.Where(col.BitwiseRightShift(1)), err: "goqu: bitwise operator 'Right Shift' not supported"},
)
}

func TestDatasetAdapterSuite(t *testing.T) {
suite.Run(t, new(sqlserverDialectSuite))
}
26 changes: 26 additions & 0 deletions docs/selecting.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* [`Window`](#window)
* [`With`](#with)
* [`SetError`](#seterror)
* [`ForUpdate`](#forupdate)
* 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
Expand Down Expand Up @@ -875,6 +876,31 @@ name is empty
name is empty
```

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

```go
sql, _, _ := goqu.From("test").ForUpdate(exp.Wait).ToSQL()
fmt.Println(sql)
```

Output:
```sql
SELECT * FROM "test" FOR UPDATE
```

If your dialect supports FOR UPDATE OF you provide tables to be locked as variable arguments to the ForUpdate method.

```go
sql, _, _ := goqu.From("test").ForUpdate(exp.Wait, goqu.T("test")).ToSQL()
fmt.Println(sql)
```

Output:
```sql
SELECT * FROM "test" FOR UPDATE OF "test"
```

## 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
89 changes: 89 additions & 0 deletions exp/bitwise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package exp

type bitwise struct {
lhs Expression
rhs interface{}
op BitwiseOperation
}

func NewBitwiseExpression(op BitwiseOperation, lhs Expression, rhs interface{}) BitwiseExpression {
return bitwise{op: op, lhs: lhs, rhs: rhs}
}

func (b bitwise) Clone() Expression {
return NewBitwiseExpression(b.op, b.lhs.Clone(), b.rhs)
}

func (b bitwise) RHS() interface{} {
return b.rhs
}

func (b bitwise) LHS() Expression {
return b.lhs
}

func (b bitwise) Op() BitwiseOperation {
return b.op
}

func (b bitwise) Expression() Expression { return b }
func (b bitwise) As(val interface{}) AliasedExpression { return NewAliasExpression(b, val) }
func (b bitwise) Eq(val interface{}) BooleanExpression { return eq(b, val) }
func (b bitwise) Neq(val interface{}) BooleanExpression { return neq(b, val) }
func (b bitwise) Gt(val interface{}) BooleanExpression { return gt(b, val) }
func (b bitwise) Gte(val interface{}) BooleanExpression { return gte(b, val) }
func (b bitwise) Lt(val interface{}) BooleanExpression { return lt(b, val) }
func (b bitwise) Lte(val interface{}) BooleanExpression { return lte(b, val) }
func (b bitwise) Asc() OrderedExpression { return asc(b) }
func (b bitwise) Desc() OrderedExpression { return desc(b) }
func (b bitwise) Like(i interface{}) BooleanExpression { return like(b, i) }
func (b bitwise) NotLike(i interface{}) BooleanExpression { return notLike(b, i) }
func (b bitwise) ILike(i interface{}) BooleanExpression { return iLike(b, i) }
func (b bitwise) NotILike(i interface{}) BooleanExpression { return notILike(b, i) }
func (b bitwise) RegexpLike(val interface{}) BooleanExpression { return regexpLike(b, val) }
func (b bitwise) RegexpNotLike(val interface{}) BooleanExpression { return regexpNotLike(b, val) }
func (b bitwise) RegexpILike(val interface{}) BooleanExpression { return regexpILike(b, val) }
func (b bitwise) RegexpNotILike(val interface{}) BooleanExpression { return regexpNotILike(b, val) }
func (b bitwise) In(i ...interface{}) BooleanExpression { return in(b, i...) }
func (b bitwise) NotIn(i ...interface{}) BooleanExpression { return notIn(b, i...) }
func (b bitwise) Is(i interface{}) BooleanExpression { return is(b, i) }
func (b bitwise) IsNot(i interface{}) BooleanExpression { return isNot(b, i) }
func (b bitwise) IsNull() BooleanExpression { return is(b, nil) }
func (b bitwise) IsNotNull() BooleanExpression { return isNot(b, nil) }
func (b bitwise) IsTrue() BooleanExpression { return is(b, true) }
func (b bitwise) IsNotTrue() BooleanExpression { return isNot(b, true) }
func (b bitwise) IsFalse() BooleanExpression { return is(b, false) }
func (b bitwise) IsNotFalse() BooleanExpression { return isNot(b, false) }
func (b bitwise) Distinct() SQLFunctionExpression { return NewSQLFunctionExpression("DISTINCT", b) }
func (b bitwise) Between(val RangeVal) RangeExpression { return between(b, val) }
func (b bitwise) NotBetween(val RangeVal) RangeExpression { return notBetween(b, val) }

// used internally to create a Bitwise Inversion BitwiseExpression
func bitwiseInversion(rhs Expression) BitwiseExpression {
return NewBitwiseExpression(BitwiseInversionOp, nil, rhs)
}

// used internally to create a Bitwise OR BitwiseExpression
func bitwiseOr(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseOrOp, lhs, rhs)
}

// used internally to create a Bitwise AND BitwiseExpression
func bitwiseAnd(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseAndOp, lhs, rhs)
}

// used internally to create a Bitwise XOR BitwiseExpression
func bitwiseXor(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseXorOp, lhs, rhs)
}

// used internally to create a Bitwise LEFT SHIFT BitwiseExpression
func bitwiseLeftShift(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseLeftShiftOp, lhs, rhs)
}

// used internally to create a Bitwise RIGHT SHIFT BitwiseExpression
func bitwiseRightShift(lhs Expression, rhs interface{}) BitwiseExpression {
return NewBitwiseExpression(BitwiseRightShiftOp, lhs, rhs)
}
84 changes: 84 additions & 0 deletions exp/bitwise_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package exp_test

import (
"testing"

"github.com/doug-martin/goqu/v9/exp"
"github.com/stretchr/testify/suite"
)

type bitwiseExpressionSuite struct {
suite.Suite
}

func TestBitwiseExpressionSuite(t *testing.T) {
suite.Run(t, &bitwiseExpressionSuite{})
}

func (bes *bitwiseExpressionSuite) TestClone() {
be := exp.NewBitwiseExpression(exp.BitwiseAndOp, exp.NewIdentifierExpression("", "", "col"), 1)
bes.Equal(be, be.Clone())
}

func (bes *bitwiseExpressionSuite) TestExpression() {
be := exp.NewBitwiseExpression(exp.BitwiseAndOp, exp.NewIdentifierExpression("", "", "col"), 1)
bes.Equal(be, be.Expression())
}

func (bes *bitwiseExpressionSuite) TestAs() {
be := exp.NewBitwiseExpression(exp.BitwiseInversionOp, exp.NewIdentifierExpression("", "", "col"), 1)
bes.Equal(exp.NewAliasExpression(be, "a"), be.As("a"))
}

func (bes *bitwiseExpressionSuite) TestAsc() {
be := exp.NewBitwiseExpression(exp.BitwiseAndOp, exp.NewIdentifierExpression("", "", "col"), 1)
bes.Equal(exp.NewOrderedExpression(be, exp.AscDir, exp.NoNullsSortType), be.Asc())
}

func (bes *bitwiseExpressionSuite) TestDesc() {
be := exp.NewBitwiseExpression(exp.BitwiseOrOp, exp.NewIdentifierExpression("", "", "col"), 1)
bes.Equal(exp.NewOrderedExpression(be, exp.DescSortDir, exp.NoNullsSortType), be.Desc())
}

func (bes *bitwiseExpressionSuite) TestAllOthers() {
be := exp.NewBitwiseExpression(exp.BitwiseRightShiftOp, exp.NewIdentifierExpression("", "", "col"), 1)
rv := exp.NewRangeVal(1, 2)
pattern := "bitwiseExp like%"
inVals := []interface{}{1, 2}
testCases := []struct {
Ex exp.Expression
Expected exp.Expression
}{
{Ex: be.Eq(1), Expected: exp.NewBooleanExpression(exp.EqOp, be, 1)},
{Ex: be.Neq(1), Expected: exp.NewBooleanExpression(exp.NeqOp, be, 1)},
{Ex: be.Gt(1), Expected: exp.NewBooleanExpression(exp.GtOp, be, 1)},
{Ex: be.Gte(1), Expected: exp.NewBooleanExpression(exp.GteOp, be, 1)},
{Ex: be.Lt(1), Expected: exp.NewBooleanExpression(exp.LtOp, be, 1)},
{Ex: be.Lte(1), Expected: exp.NewBooleanExpression(exp.LteOp, be, 1)},
{Ex: be.Between(rv), Expected: exp.NewRangeExpression(exp.BetweenOp, be, rv)},
{Ex: be.NotBetween(rv), Expected: exp.NewRangeExpression(exp.NotBetweenOp, be, rv)},
{Ex: be.Like(pattern), Expected: exp.NewBooleanExpression(exp.LikeOp, be, pattern)},
{Ex: be.NotLike(pattern), Expected: exp.NewBooleanExpression(exp.NotLikeOp, be, pattern)},
{Ex: be.ILike(pattern), Expected: exp.NewBooleanExpression(exp.ILikeOp, be, pattern)},
{Ex: be.NotILike(pattern), Expected: exp.NewBooleanExpression(exp.NotILikeOp, be, pattern)},
{Ex: be.RegexpLike(pattern), Expected: exp.NewBooleanExpression(exp.RegexpLikeOp, be, pattern)},
{Ex: be.RegexpNotLike(pattern), Expected: exp.NewBooleanExpression(exp.RegexpNotLikeOp, be, pattern)},
{Ex: be.RegexpILike(pattern), Expected: exp.NewBooleanExpression(exp.RegexpILikeOp, be, pattern)},
{Ex: be.RegexpNotILike(pattern), Expected: exp.NewBooleanExpression(exp.RegexpNotILikeOp, be, pattern)},
{Ex: be.In(inVals), Expected: exp.NewBooleanExpression(exp.InOp, be, inVals)},
{Ex: be.NotIn(inVals), Expected: exp.NewBooleanExpression(exp.NotInOp, be, inVals)},
{Ex: be.Is(true), Expected: exp.NewBooleanExpression(exp.IsOp, be, true)},
{Ex: be.IsNot(true), Expected: exp.NewBooleanExpression(exp.IsNotOp, be, true)},
{Ex: be.IsNull(), Expected: exp.NewBooleanExpression(exp.IsOp, be, nil)},
{Ex: be.IsNotNull(), Expected: exp.NewBooleanExpression(exp.IsNotOp, be, nil)},
{Ex: be.IsTrue(), Expected: exp.NewBooleanExpression(exp.IsOp, be, true)},
{Ex: be.IsNotTrue(), Expected: exp.NewBooleanExpression(exp.IsNotOp, be, true)},
{Ex: be.IsFalse(), Expected: exp.NewBooleanExpression(exp.IsOp, be, false)},
{Ex: be.IsNotFalse(), Expected: exp.NewBooleanExpression(exp.IsNotOp, be, false)},
{Ex: be.Distinct(), Expected: exp.NewSQLFunctionExpression("DISTINCT", be)},
}

for _, tc := range testCases {
bes.Equal(tc.Expected, tc.Ex)
}
}
Loading