Skip to content

Commit

Permalink
Merge pull request #407 from upper/tests-for-comparison-operators
Browse files Browse the repository at this point in the history
db: draft tests for comparison operators
  • Loading branch information
José Nieto authored Sep 24, 2017
2 parents 925e375 + 389eb88 commit 4ceccb1
Show file tree
Hide file tree
Showing 12 changed files with 597 additions and 121 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ SHELL := /bin/bash
WAPPER ?= all
DB_HOST ?= 127.0.0.1

TEST_FLAGS ?=

export DB_HOST
export WRAPPER

Expand Down Expand Up @@ -33,7 +35,7 @@ reset-db:
$(MAKE) -C mongo reset-db

test-main: reset-db
go test -v ./tests/...
go test $(TEST_FLAGS) -v ./tests/...

test: test-adapters test-libs test-main

Expand Down
52 changes: 33 additions & 19 deletions comparison.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
package db

import (
"reflect"
"time"
)

Expand Down Expand Up @@ -60,16 +61,9 @@ const (

ComparisonOperatorLike
ComparisonOperatorNotLike
ComparisonOperatorILike
ComparisonOperatorNotILike

ComparisonOperatorRegExp
ComparisonOperatorNotRegExp
ComparisonOperatorIRegExp
ComparisonOperatorNotIRegExp

ComparisonOperatorIsDistinctFrom
ComparisonOperatorIsNotDistinctFrom

ComparisonOperatorAfter
ComparisonOperatorBefore
Expand Down Expand Up @@ -150,30 +144,30 @@ func Lt(v interface{}) Comparison {
func In(v interface{}) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorIn,
v: v,
v: toInterfaceArray(v),
}
}

// NotIn indicates whether the argument is not part of the reference.
func NotIn(v interface{}) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorNotIn,
v: v,
v: toInterfaceArray(v),
}
}

// After indicates whether the reference is after the given time.
func After(t time.Time) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorAfter,
t: ComparisonOperatorGreaterThan,
v: t,
}
}

// Before indicates whether the reference is before the given time.
func Before(t time.Time) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorBefore,
t: ComparisonOperatorLessThan,
v: t,
}
}
Expand All @@ -182,7 +176,7 @@ func Before(t time.Time) Comparison {
// time value.
func OnOrAfter(t time.Time) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorOnOrAfter,
t: ComparisonOperatorGreaterThanOrEqualTo,
v: t,
}
}
Expand All @@ -191,7 +185,7 @@ func OnOrAfter(t time.Time) Comparison {
// time value.
func OnOrBefore(t time.Time) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorOnOrBefore,
t: ComparisonOperatorLessThanOrEqualTo,
v: t,
}
}
Expand Down Expand Up @@ -240,6 +234,7 @@ func IsNotNull() Comparison {
return IsNot(nil)
}

/*
// IsDistinctFrom indicates whether the reference is different from
// the given value, including NULL values.
func IsDistinctFrom(v interface{}) Comparison {
Expand All @@ -257,26 +252,28 @@ func IsNotDistinctFrom(v interface{}) Comparison {
v: v,
}
}
*/

// Like indicates whether the reference matches the wildcard value.
func Like(v interface{}) Comparison {
func Like(v string) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorLike,
v: v,
}
}

// NotLike indicates whether the reference does not match the wildcard value.
func NotLike(v interface{}) Comparison {
func NotLike(v string) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorNotLike,
v: v,
}
}

/*
// ILike indicates whether the reference matches the wildcard value (case
// insensitive).
func ILike(v interface{}) Comparison {
func ILike(v string) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorILike,
v: v,
Expand All @@ -285,23 +282,24 @@ func ILike(v interface{}) Comparison {
// NotILike indicates whether the reference does not match the wildcard value
// (case insensitive).
func NotILike(v interface{}) Comparison {
func NotILike(v string) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorNotILike,
v: v,
}
}
*/

// RegExp indicates whether the reference matches the regexp pattern.
func RegExp(v interface{}) Comparison {
func RegExp(v string) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorRegExp,
v: v,
}
}

// NotRegExp indicates whether the reference does not match the regexp pattern.
func NotRegExp(v interface{}) Comparison {
func NotRegExp(v string) Comparison {
return &dbComparisonOperator{
t: ComparisonOperatorNotRegExp,
v: v,
Expand All @@ -317,4 +315,20 @@ func Op(customOperator string, v interface{}) Comparison {
}
}

func toInterfaceArray(v interface{}) []interface{} {
rv := reflect.ValueOf(v)
switch rv.Type().Kind() {
case reflect.Ptr:
return toInterfaceArray(rv.Elem().Interface())
case reflect.Slice:
elems := rv.Len()
args := make([]interface{}, elems)
for i := 0; i < elems; i++ {
args[i] = rv.Index(i).Interface()
}
return args
}
return []interface{}{v}
}

var _ Comparison = &dbComparisonOperator{}
4 changes: 2 additions & 2 deletions internal/sqladapter/exql/column_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func (c *ColumnValue) Compile(layout *Template) (compiled string, err error) {
}
}

compiled = mustParse(layout.ColumnValue, data)
compiled = strings.TrimSpace(mustParse(layout.ColumnValue, data))

layout.Write(c, compiled)

Expand Down Expand Up @@ -100,7 +100,7 @@ func (c *ColumnValues) Compile(layout *Template) (compiled string, err error) {
}
}

compiled = strings.Join(out, layout.IdentifierSeparator)
compiled = strings.TrimSpace(strings.Join(out, layout.IdentifierSeparator))

layout.Write(c, compiled)

Expand Down
3 changes: 2 additions & 1 deletion internal/sqladapter/exql/raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package exql

import (
"fmt"
"strings"
)

var (
Expand All @@ -16,7 +17,7 @@ type Raw struct {

// RawValue creates and returns a new raw value.
func RawValue(v string) *Raw {
return &Raw{Value: v}
return &Raw{Value: strings.TrimSpace(v)}
}

// Hash returns a unique identifier for the struct.
Expand Down
104 changes: 65 additions & 39 deletions lib/sqlbuilder/comparison.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,58 @@
package sqlbuilder

import (
"fmt"
"strings"

"upper.io/db.v3"
"upper.io/db.v3/internal/sqladapter/exql"
)

var comparisonOperators = map[db.ComparisonOperator]string{
db.ComparisonOperatorEqual: "=",
db.ComparisonOperatorNotEqual: "!=",
db.ComparisonOperatorGreaterThanOrEqualTo: ">=",
db.ComparisonOperatorEqual: "=",
db.ComparisonOperatorNotEqual: "!=",

db.ComparisonOperatorLessThan: "<",
db.ComparisonOperatorGreaterThan: ">",

db.ComparisonOperatorLessThanOrEqualTo: "<=",
db.ComparisonOperatorLessThan: "<",
db.ComparisonOperatorGreaterThan: ">",
db.ComparisonOperatorBetween: "BETWEEN",
db.ComparisonOperatorIs: "IS",
db.ComparisonOperatorIsNot: "IS NOT",
db.ComparisonOperatorIn: "IN",
db.ComparisonOperatorNotIn: "NOT IN",
db.ComparisonOperatorIsDistinctFrom: "IS DISTINCT FROM",
db.ComparisonOperatorIsNotDistinctFrom: "IS NOT DISTINCT FROM",
db.ComparisonOperatorGreaterThanOrEqualTo: ">=",

db.ComparisonOperatorBetween: "BETWEEN",
db.ComparisonOperatorNotBetween: "NOT BETWEEN",

db.ComparisonOperatorIn: "IN",
db.ComparisonOperatorNotIn: "NOT IN",

db.ComparisonOperatorIs: "IS",
db.ComparisonOperatorIsNot: "IS NOT",

db.ComparisonOperatorLike: "LIKE",
db.ComparisonOperatorNotLike: "NOT LIKE",

db.ComparisonOperatorRegExp: "REGEXP",
db.ComparisonOperatorNotRegExp: "NOT REGEXP",
}

type hasCustomOperator interface {
CustomOperator() string
}

type operatorWrapper struct {
tu *templateWithUtils
op db.Comparison
customOp string
v interface{}
tu *templateWithUtils
cv *exql.ColumnValue

op db.Comparison
v interface{}
}

func (ow *operatorWrapper) cmp() db.Comparison {
if ow.op != nil {
return ow.op
}

if ow.customOp != "" {
return db.Op(ow.customOp, ow.v)
if ow.cv.Operator != "" {
return db.Op(ow.cv.Operator, ow.v)
}

if ow.v == nil {
Expand All @@ -54,42 +67,55 @@ func (ow *operatorWrapper) cmp() db.Comparison {
return db.Eq(ow.v)
}

func (ow *operatorWrapper) build() (string, string, []interface{}) {
cmp := ow.cmp()
func (ow *operatorWrapper) preprocess() (string, []interface{}) {
placeholder := "?"

op := ow.tu.comparisonOperatorMapper(cmp.Operator())
column, err := ow.cv.Column.Compile(ow.tu.Template)
if err != nil {
panic(fmt.Sprintf("could not compile column: %v", err.Error()))
}

c := ow.cmp()

op := ow.tu.comparisonOperatorMapper(c.Operator())

switch cmp.Operator() {
var args []interface{}

switch c.Operator() {
case db.ComparisonOperatorNone:
if c, ok := cmp.(hasCustomOperator); ok {
if c, ok := c.(hasCustomOperator); ok {
op = c.CustomOperator()
} else {
panic("no operator given")
}
case db.ComparisonOperatorIn, db.ComparisonOperatorNotIn:
values := cmp.Value().([]interface{})
values := c.Value().([]interface{})
if len(values) < 1 {
return op, "(NULL)", nil
}
if len(values) > 0 {
format := "(?" + strings.Repeat(", ?", len(values)-1) + ")"
return op, format, values
placeholder, args = "(NULL)", []interface{}{}
break
}
return op, "(NULL)", nil
placeholder, args = "(?"+strings.Repeat(", ?", len(values)-1)+")", values
case db.ComparisonOperatorIs, db.ComparisonOperatorIsNot:
switch cmp.Value() {
switch c.Value() {
case nil:
return op, "NULL", nil
placeholder, args = "NULL", []interface{}{}
case false:
return op, "FALSE", nil
placeholder, args = "FALSE", []interface{}{}
case true:
return op, "TRUE", nil
placeholder, args = "TRUE", []interface{}{}
}
case db.ComparisonOperatorBetween:
values := cmp.Value()
return op, "? AND ?", values.([]interface{})
case db.ComparisonOperatorBetween, db.ComparisonOperatorNotBetween:
values := c.Value().([]interface{})
placeholder, args = "? AND ?", []interface{}{values[0], values[1]}
}

if args == nil {
args = []interface{}{c.Value()}
}

if strings.Contains(op, ":column") {
return strings.Replace(op, ":column", column, -1), args
}

v := cmp.Value()
return op, "?", []interface{}{v}
return column + " " + op + " " + placeholder, args
}
1 change: 1 addition & 0 deletions lib/sqlbuilder/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package sqlbuilder

import (
"database/sql"

"upper.io/db.v3"
)

Expand Down
Loading

0 comments on commit 4ceccb1

Please sign in to comment.