diff --git a/README.md b/README.md index 9ba4c8e1..4b14f395 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,8 @@ One common reason to use this is to prevent string concatenation in a loop. * Gte * Lt * Lte +* Like +* NotLike ```go dbr.And( diff --git a/condition.go b/condition.go index ff299793..c6a59404 100644 --- a/condition.go +++ b/condition.go @@ -1,6 +1,9 @@ package dbr -import "reflect" +import ( + "fmt" + "reflect" +) func buildCond(d Dialect, buf Buffer, pred string, cond ...Builder) error { for i, c := range cond { @@ -117,3 +120,51 @@ func Lte(column string, value interface{}) Builder { return buildCmp(d, buf, "<=", column, value) }) } + +func buildLikeCmp(d Dialect, buf Buffer, pred string, column string, value interface{}) error { + if value == nil { + return fmt.Errorf("Column %s %s nil is Not Supported", column, pred) + } + + v := reflect.ValueOf(value) + switch v.Kind() { + case reflect.String: + // pass as is + return buildCmp(d, buf, pred, column, value) + case reflect.Ptr, reflect.Interface: // pointer or interface + // for pointers & interfaces check + return buildLikeCmp(d, buf, pred, column, v.Elem().Interface()) + case reflect.Slice: + switch v.Type().Elem().Kind() { + case reflect.Uint8: // bytes + // interpolator will handle this case + return buildCmp(d, buf, pred, column, value) + case reflect.Int32: // rune + // need to convert into string + return buildCmp(d, buf, pred, column, string(value.([]rune))) + } + fallthrough + default: + return fmt.Errorf("Column %s %s Invalid Value, string expected", column, pred) + } +} + +// Lk is `LIKE`. +// When value is nil, do nothing. +// When value is a slice, do nothing. +// Otherwise it will be translated to `LIKE`. +func Like(column string, value interface{}) Builder { + return BuildFunc(func(d Dialect, buf Buffer) error { + return buildLikeCmp(d, buf, "LIKE", column, value) + }) +} + +// Nlk is `NOT LIKE`. +// When value is nil, do nothing. +// When value is a slice, do nothing. +// Otherwise it will be translated to `NOT LIKE`. +func NotLike(column string, value interface{}) Builder { + return BuildFunc(func(d Dialect, buf Buffer) error { + return buildLikeCmp(d, buf, "NOT LIKE", column, value) + }) +} diff --git a/condition_test.go b/condition_test.go index a25f3fcc..fa54c158 100644 --- a/condition_test.go +++ b/condition_test.go @@ -12,61 +12,134 @@ func TestCondition(t *testing.T) { cond Builder query string value []interface{} + isErr bool }{ { cond: Eq("col", 1), query: "`col` = ?", value: []interface{}{1}, + isErr: false, }, { cond: Eq("col", nil), query: "`col` IS NULL", value: nil, + isErr: false, }, { cond: Eq("col", []int{}), query: "0", value: nil, + isErr: false, }, { cond: Neq("col", 1), query: "`col` != ?", value: []interface{}{1}, + isErr: false, }, { cond: Neq("col", nil), query: "`col` IS NOT NULL", value: nil, + isErr: false, }, { cond: Gt("col", 1), query: "`col` > ?", value: []interface{}{1}, + isErr: false, }, { cond: Gte("col", 1), query: "`col` >= ?", value: []interface{}{1}, + isErr: false, }, { cond: Lt("col", 1), query: "`col` < ?", value: []interface{}{1}, + isErr: false, }, { cond: Lte("col", 1), query: "`col` <= ?", value: []interface{}{1}, + isErr: false, + }, + { + cond: Like("col", 1), + query: "", + value: nil, + isErr: true, + }, + { + cond: Like("col", "like"), + query: "`col` LIKE ?", + value: []interface{}{"like"}, + isErr: false, + }, + { + cond: Like("col", []rune{'l', 'i', 'k', 'e'}), + query: "`col` LIKE ?", + value: []interface{}{"like"}, + isErr: false, + }, + { + cond: Like("col", []int{}), + query: "", + value: nil, + isErr: true, + }, + { + cond: Like("col", nil), + query: "", + value: nil, + isErr: true, + }, + { + cond: NotLike("col", 1), + query: "", + value: nil, + isErr: true, + }, + { + cond: NotLike("col", "not like"), + query: "`col` NOT LIKE ?", + value: []interface{}{"not like"}, + isErr: false, + }, + { + cond: NotLike("col", []rune{'n', 'o', 't', ' ', 'l', 'i', 'k', 'e'}), + query: "`col` NOT LIKE ?", + value: []interface{}{"not like"}, + isErr: false, + }, + { + cond: NotLike("col", []int{}), + query: "", + value: nil, + isErr: true, + }, + { + cond: NotLike("col", nil), + query: "", + value: nil, + isErr: true, }, { cond: And(Lt("a", 1), Or(Gt("b", 2), Neq("c", 3))), query: "(`a` < ?) AND ((`b` > ?) OR (`c` != ?))", value: []interface{}{1, 2, 3}, + isErr: false, }, } { buf := NewBuffer() err := test.cond.Build(dialect.MySQL, buf) - assert.NoError(t, err) + if !test.isErr { + assert.NoError(t, err) + } assert.Equal(t, test.query, buf.String()) assert.Equal(t, test.value, buf.Value()) }