Skip to content

Commit 4ceccb1

Browse files
author
José Nieto
authored
Merge pull request #407 from upper/tests-for-comparison-operators
db: draft tests for comparison operators
2 parents 925e375 + 389eb88 commit 4ceccb1

File tree

12 files changed

+597
-121
lines changed

12 files changed

+597
-121
lines changed

Diff for: Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ SHELL := /bin/bash
33
WAPPER ?= all
44
DB_HOST ?= 127.0.0.1
55

6+
TEST_FLAGS ?=
7+
68
export DB_HOST
79
export WRAPPER
810

@@ -33,7 +35,7 @@ reset-db:
3335
$(MAKE) -C mongo reset-db
3436

3537
test-main: reset-db
36-
go test -v ./tests/...
38+
go test $(TEST_FLAGS) -v ./tests/...
3739

3840
test: test-adapters test-libs test-main
3941

Diff for: comparison.go

+33-19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
package db
2323

2424
import (
25+
"reflect"
2526
"time"
2627
)
2728

@@ -60,16 +61,9 @@ const (
6061

6162
ComparisonOperatorLike
6263
ComparisonOperatorNotLike
63-
ComparisonOperatorILike
64-
ComparisonOperatorNotILike
6564

6665
ComparisonOperatorRegExp
6766
ComparisonOperatorNotRegExp
68-
ComparisonOperatorIRegExp
69-
ComparisonOperatorNotIRegExp
70-
71-
ComparisonOperatorIsDistinctFrom
72-
ComparisonOperatorIsNotDistinctFrom
7367

7468
ComparisonOperatorAfter
7569
ComparisonOperatorBefore
@@ -150,30 +144,30 @@ func Lt(v interface{}) Comparison {
150144
func In(v interface{}) Comparison {
151145
return &dbComparisonOperator{
152146
t: ComparisonOperatorIn,
153-
v: v,
147+
v: toInterfaceArray(v),
154148
}
155149
}
156150

157151
// NotIn indicates whether the argument is not part of the reference.
158152
func NotIn(v interface{}) Comparison {
159153
return &dbComparisonOperator{
160154
t: ComparisonOperatorNotIn,
161-
v: v,
155+
v: toInterfaceArray(v),
162156
}
163157
}
164158

165159
// After indicates whether the reference is after the given time.
166160
func After(t time.Time) Comparison {
167161
return &dbComparisonOperator{
168-
t: ComparisonOperatorAfter,
162+
t: ComparisonOperatorGreaterThan,
169163
v: t,
170164
}
171165
}
172166

173167
// Before indicates whether the reference is before the given time.
174168
func Before(t time.Time) Comparison {
175169
return &dbComparisonOperator{
176-
t: ComparisonOperatorBefore,
170+
t: ComparisonOperatorLessThan,
177171
v: t,
178172
}
179173
}
@@ -182,7 +176,7 @@ func Before(t time.Time) Comparison {
182176
// time value.
183177
func OnOrAfter(t time.Time) Comparison {
184178
return &dbComparisonOperator{
185-
t: ComparisonOperatorOnOrAfter,
179+
t: ComparisonOperatorGreaterThanOrEqualTo,
186180
v: t,
187181
}
188182
}
@@ -191,7 +185,7 @@ func OnOrAfter(t time.Time) Comparison {
191185
// time value.
192186
func OnOrBefore(t time.Time) Comparison {
193187
return &dbComparisonOperator{
194-
t: ComparisonOperatorOnOrBefore,
188+
t: ComparisonOperatorLessThanOrEqualTo,
195189
v: t,
196190
}
197191
}
@@ -240,6 +234,7 @@ func IsNotNull() Comparison {
240234
return IsNot(nil)
241235
}
242236

237+
/*
243238
// IsDistinctFrom indicates whether the reference is different from
244239
// the given value, including NULL values.
245240
func IsDistinctFrom(v interface{}) Comparison {
@@ -257,26 +252,28 @@ func IsNotDistinctFrom(v interface{}) Comparison {
257252
v: v,
258253
}
259254
}
255+
*/
260256

261257
// Like indicates whether the reference matches the wildcard value.
262-
func Like(v interface{}) Comparison {
258+
func Like(v string) Comparison {
263259
return &dbComparisonOperator{
264260
t: ComparisonOperatorLike,
265261
v: v,
266262
}
267263
}
268264

269265
// NotLike indicates whether the reference does not match the wildcard value.
270-
func NotLike(v interface{}) Comparison {
266+
func NotLike(v string) Comparison {
271267
return &dbComparisonOperator{
272268
t: ComparisonOperatorNotLike,
273269
v: v,
274270
}
275271
}
276272

273+
/*
277274
// ILike indicates whether the reference matches the wildcard value (case
278275
// insensitive).
279-
func ILike(v interface{}) Comparison {
276+
func ILike(v string) Comparison {
280277
return &dbComparisonOperator{
281278
t: ComparisonOperatorILike,
282279
v: v,
@@ -285,23 +282,24 @@ func ILike(v interface{}) Comparison {
285282
286283
// NotILike indicates whether the reference does not match the wildcard value
287284
// (case insensitive).
288-
func NotILike(v interface{}) Comparison {
285+
func NotILike(v string) Comparison {
289286
return &dbComparisonOperator{
290287
t: ComparisonOperatorNotILike,
291288
v: v,
292289
}
293290
}
291+
*/
294292

295293
// RegExp indicates whether the reference matches the regexp pattern.
296-
func RegExp(v interface{}) Comparison {
294+
func RegExp(v string) Comparison {
297295
return &dbComparisonOperator{
298296
t: ComparisonOperatorRegExp,
299297
v: v,
300298
}
301299
}
302300

303301
// NotRegExp indicates whether the reference does not match the regexp pattern.
304-
func NotRegExp(v interface{}) Comparison {
302+
func NotRegExp(v string) Comparison {
305303
return &dbComparisonOperator{
306304
t: ComparisonOperatorNotRegExp,
307305
v: v,
@@ -317,4 +315,20 @@ func Op(customOperator string, v interface{}) Comparison {
317315
}
318316
}
319317

318+
func toInterfaceArray(v interface{}) []interface{} {
319+
rv := reflect.ValueOf(v)
320+
switch rv.Type().Kind() {
321+
case reflect.Ptr:
322+
return toInterfaceArray(rv.Elem().Interface())
323+
case reflect.Slice:
324+
elems := rv.Len()
325+
args := make([]interface{}, elems)
326+
for i := 0; i < elems; i++ {
327+
args[i] = rv.Index(i).Interface()
328+
}
329+
return args
330+
}
331+
return []interface{}{v}
332+
}
333+
320334
var _ Comparison = &dbComparisonOperator{}

Diff for: internal/sqladapter/exql/column_value.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (c *ColumnValue) Compile(layout *Template) (compiled string, err error) {
4848
}
4949
}
5050

51-
compiled = mustParse(layout.ColumnValue, data)
51+
compiled = strings.TrimSpace(mustParse(layout.ColumnValue, data))
5252

5353
layout.Write(c, compiled)
5454

@@ -100,7 +100,7 @@ func (c *ColumnValues) Compile(layout *Template) (compiled string, err error) {
100100
}
101101
}
102102

103-
compiled = strings.Join(out, layout.IdentifierSeparator)
103+
compiled = strings.TrimSpace(strings.Join(out, layout.IdentifierSeparator))
104104

105105
layout.Write(c, compiled)
106106

Diff for: internal/sqladapter/exql/raw.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package exql
22

33
import (
44
"fmt"
5+
"strings"
56
)
67

78
var (
@@ -16,7 +17,7 @@ type Raw struct {
1617

1718
// RawValue creates and returns a new raw value.
1819
func RawValue(v string) *Raw {
19-
return &Raw{Value: v}
20+
return &Raw{Value: strings.TrimSpace(v)}
2021
}
2122

2223
// Hash returns a unique identifier for the struct.

Diff for: lib/sqlbuilder/comparison.go

+65-39
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,58 @@
11
package sqlbuilder
22

33
import (
4+
"fmt"
45
"strings"
56

67
"upper.io/db.v3"
8+
"upper.io/db.v3/internal/sqladapter/exql"
79
)
810

911
var comparisonOperators = map[db.ComparisonOperator]string{
10-
db.ComparisonOperatorEqual: "=",
11-
db.ComparisonOperatorNotEqual: "!=",
12-
db.ComparisonOperatorGreaterThanOrEqualTo: ">=",
12+
db.ComparisonOperatorEqual: "=",
13+
db.ComparisonOperatorNotEqual: "!=",
14+
15+
db.ComparisonOperatorLessThan: "<",
16+
db.ComparisonOperatorGreaterThan: ">",
17+
1318
db.ComparisonOperatorLessThanOrEqualTo: "<=",
14-
db.ComparisonOperatorLessThan: "<",
15-
db.ComparisonOperatorGreaterThan: ">",
16-
db.ComparisonOperatorBetween: "BETWEEN",
17-
db.ComparisonOperatorIs: "IS",
18-
db.ComparisonOperatorIsNot: "IS NOT",
19-
db.ComparisonOperatorIn: "IN",
20-
db.ComparisonOperatorNotIn: "NOT IN",
21-
db.ComparisonOperatorIsDistinctFrom: "IS DISTINCT FROM",
22-
db.ComparisonOperatorIsNotDistinctFrom: "IS NOT DISTINCT FROM",
19+
db.ComparisonOperatorGreaterThanOrEqualTo: ">=",
20+
21+
db.ComparisonOperatorBetween: "BETWEEN",
22+
db.ComparisonOperatorNotBetween: "NOT BETWEEN",
23+
24+
db.ComparisonOperatorIn: "IN",
25+
db.ComparisonOperatorNotIn: "NOT IN",
26+
27+
db.ComparisonOperatorIs: "IS",
28+
db.ComparisonOperatorIsNot: "IS NOT",
29+
30+
db.ComparisonOperatorLike: "LIKE",
31+
db.ComparisonOperatorNotLike: "NOT LIKE",
32+
33+
db.ComparisonOperatorRegExp: "REGEXP",
34+
db.ComparisonOperatorNotRegExp: "NOT REGEXP",
2335
}
2436

2537
type hasCustomOperator interface {
2638
CustomOperator() string
2739
}
2840

2941
type operatorWrapper struct {
30-
tu *templateWithUtils
31-
op db.Comparison
32-
customOp string
33-
v interface{}
42+
tu *templateWithUtils
43+
cv *exql.ColumnValue
44+
45+
op db.Comparison
46+
v interface{}
3447
}
3548

3649
func (ow *operatorWrapper) cmp() db.Comparison {
3750
if ow.op != nil {
3851
return ow.op
3952
}
4053

41-
if ow.customOp != "" {
42-
return db.Op(ow.customOp, ow.v)
54+
if ow.cv.Operator != "" {
55+
return db.Op(ow.cv.Operator, ow.v)
4356
}
4457

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

57-
func (ow *operatorWrapper) build() (string, string, []interface{}) {
58-
cmp := ow.cmp()
70+
func (ow *operatorWrapper) preprocess() (string, []interface{}) {
71+
placeholder := "?"
5972

60-
op := ow.tu.comparisonOperatorMapper(cmp.Operator())
73+
column, err := ow.cv.Column.Compile(ow.tu.Template)
74+
if err != nil {
75+
panic(fmt.Sprintf("could not compile column: %v", err.Error()))
76+
}
77+
78+
c := ow.cmp()
79+
80+
op := ow.tu.comparisonOperatorMapper(c.Operator())
6181

62-
switch cmp.Operator() {
82+
var args []interface{}
83+
84+
switch c.Operator() {
6385
case db.ComparisonOperatorNone:
64-
if c, ok := cmp.(hasCustomOperator); ok {
86+
if c, ok := c.(hasCustomOperator); ok {
6587
op = c.CustomOperator()
6688
} else {
6789
panic("no operator given")
6890
}
6991
case db.ComparisonOperatorIn, db.ComparisonOperatorNotIn:
70-
values := cmp.Value().([]interface{})
92+
values := c.Value().([]interface{})
7193
if len(values) < 1 {
72-
return op, "(NULL)", nil
73-
}
74-
if len(values) > 0 {
75-
format := "(?" + strings.Repeat(", ?", len(values)-1) + ")"
76-
return op, format, values
94+
placeholder, args = "(NULL)", []interface{}{}
95+
break
7796
}
78-
return op, "(NULL)", nil
97+
placeholder, args = "(?"+strings.Repeat(", ?", len(values)-1)+")", values
7998
case db.ComparisonOperatorIs, db.ComparisonOperatorIsNot:
80-
switch cmp.Value() {
99+
switch c.Value() {
81100
case nil:
82-
return op, "NULL", nil
101+
placeholder, args = "NULL", []interface{}{}
83102
case false:
84-
return op, "FALSE", nil
103+
placeholder, args = "FALSE", []interface{}{}
85104
case true:
86-
return op, "TRUE", nil
105+
placeholder, args = "TRUE", []interface{}{}
87106
}
88-
case db.ComparisonOperatorBetween:
89-
values := cmp.Value()
90-
return op, "? AND ?", values.([]interface{})
107+
case db.ComparisonOperatorBetween, db.ComparisonOperatorNotBetween:
108+
values := c.Value().([]interface{})
109+
placeholder, args = "? AND ?", []interface{}{values[0], values[1]}
110+
}
111+
112+
if args == nil {
113+
args = []interface{}{c.Value()}
114+
}
115+
116+
if strings.Contains(op, ":column") {
117+
return strings.Replace(op, ":column", column, -1), args
91118
}
92119

93-
v := cmp.Value()
94-
return op, "?", []interface{}{v}
120+
return column + " " + op + " " + placeholder, args
95121
}

Diff for: lib/sqlbuilder/scanner.go

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package sqlbuilder
2323

2424
import (
2525
"database/sql"
26+
2627
"upper.io/db.v3"
2728
)
2829

0 commit comments

Comments
 (0)