Skip to content

Commit

Permalink
Merge pull request #1 from doug-martin/master
Browse files Browse the repository at this point in the history
sync from origin
  • Loading branch information
XIELongDragon authored Oct 9, 2021
2 parents 72349ff + a7630f7 commit 90a0aeb
Show file tree
Hide file tree
Showing 25 changed files with 637 additions and 14 deletions.
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

0 comments on commit 90a0aeb

Please sign in to comment.