Skip to content

Commit 4262e4c

Browse files
authored
fix(query): preserve grouping for nested conditions (#83)
1 parent 68aea8c commit 4262e4c

2 files changed

Lines changed: 51 additions & 3 deletions

File tree

query/query.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ func (c *cond) Or(str string, args ...any) {
111111
}
112112

113113
func (c *cond) AndCond(other Condition) {
114-
c.append("AND", other)
114+
c.append("AND", groupedQuery{inner: other})
115115
}
116116

117117
func (c *cond) OrCond(other Condition) {
118-
c.append("OR", other)
118+
c.append("OR", groupedQuery{inner: other})
119119
}
120120

121121
func (c *cond) Query() (string, []any, error) {
@@ -133,6 +133,21 @@ func (c *cond) append(sep string, other ...Query) {
133133
}
134134
}
135135

136+
type groupedQuery struct {
137+
inner Query
138+
}
139+
140+
func (g groupedQuery) Query() (string, []any, error) {
141+
stmt, args, err := g.inner.Query()
142+
if err != nil {
143+
return "", nil, err
144+
}
145+
if err := guardQuery(stmt); err != nil {
146+
return "", nil, err
147+
}
148+
return "(" + stmt + ")", args, nil
149+
}
150+
136151
type chain struct {
137152
joiner string
138153
list []Query

query/query_test.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ import (
66
q "github.com/loilo-inc/exql/v3/query"
77
)
88

9+
type emptyCondition struct{}
10+
11+
func (emptyCondition) Query() (string, []any, error) {
12+
return "", nil, nil
13+
}
14+
15+
func (emptyCondition) And(string, ...any) {}
16+
17+
func (emptyCondition) Or(string, ...any) {}
18+
19+
func (emptyCondition) AndCond(q.Condition) {}
20+
21+
func (emptyCondition) OrCond(q.Condition) {}
22+
923
func TestQuery(t *testing.T) {
1024
assertQuery(t, q.V(1, 2), "?,?", 1, 2)
1125
assertQuery(t, q.Vals([]int{1, 2}), "?,?", 1, 2)
@@ -47,12 +61,31 @@ func TestCondition(t *testing.T) {
4761
cond.AndCond(q.Cond("foo = ?", "foo"))
4862
cond.OrCond(q.Cond("var = ?", "var"))
4963
assertQuery(t, cond,
50-
"id = ? AND name = ? OR age in (?,?) AND foo = ? OR var = ?",
64+
"id = ? AND name = ? OR age in (?,?) AND (foo = ?) OR (var = ?)",
5165
1, "go", 20, 21, "foo", "var",
5266
)
5367
})
68+
t.Run("nested conditions are grouped", func(t *testing.T) {
69+
tenantScoped := q.Cond("tenant_id = ?", 1)
70+
byIDOrEmail := q.Cond("id = ?", 10)
71+
byIDOrEmail.Or("email = ?", "user@example.com")
72+
73+
tenantScoped.AndCond(byIDOrEmail)
74+
75+
assertQuery(t, tenantScoped,
76+
"tenant_id = ? AND (id = ? OR email = ?)",
77+
1, 10, "user@example.com",
78+
)
79+
})
80+
5481
t.Run("should error if query retuerned an error", func(t *testing.T) {
5582
cond := q.CondFrom(q.Q(""))
5683
assertQueryErr(t, cond, "DANGER: empty query")
5784
})
85+
86+
t.Run("should error if grouped condition returned an empty query", func(t *testing.T) {
87+
cond := q.Cond("id = ?", 1)
88+
cond.AndCond(emptyCondition{})
89+
assertQueryErr(t, cond, "DANGER: empty query")
90+
})
5891
}

0 commit comments

Comments
 (0)