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

Add Bitwise operation #303

Merged
merged 1 commit into from
Oct 6, 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
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
6 changes: 6 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 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
6 changes: 6 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
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))
}
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 := "cast 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