Skip to content

Commit 2253d89

Browse files
authored
Merge pull request #190 from huandu/feature/prevent-syntax-error-with-zero-values
Ignore empty values and expressions to prevent syntax error
2 parents b0b0b02 + 06070c6 commit 2253d89

8 files changed

+193
-129
lines changed

cond.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ func (c *Cond) In(field string, values ...interface{}) string {
190190
return ""
191191
}
192192

193+
// Empty values means "false".
194+
if len(values) == 0 {
195+
return "0 = 1"
196+
}
197+
193198
return c.Var(condBuilder{
194199
Builder: func(ctx *argsCompileContext) {
195200
ctx.WriteString(field)
@@ -202,7 +207,7 @@ func (c *Cond) In(field string, values ...interface{}) string {
202207

203208
// NotIn is used to construct the expression "field NOT IN (value...)".
204209
func (c *Cond) NotIn(field string, values ...interface{}) string {
205-
if len(field) == 0 {
210+
if len(field) == 0 || len(values) == 0 {
206211
return ""
207212
}
208213

@@ -369,6 +374,8 @@ func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
369374

370375
// Or is used to construct the expression OR logic like "expr1 OR expr2 OR expr3".
371376
func (c *Cond) Or(orExpr ...string) string {
377+
orExpr = filterEmptyStrings(orExpr)
378+
372379
if len(orExpr) == 0 {
373380
return ""
374381
}
@@ -392,6 +399,8 @@ func (c *Cond) Or(orExpr ...string) string {
392399

393400
// And is used to construct the expression AND logic like "expr1 AND expr2 AND expr3".
394401
func (c *Cond) And(andExpr ...string) string {
402+
andExpr = filterEmptyStrings(andExpr)
403+
395404
if len(andExpr) == 0 {
396405
return ""
397406
}
@@ -457,6 +466,11 @@ func (c *Cond) Any(field, op string, values ...interface{}) string {
457466
return ""
458467
}
459468

469+
// Empty values means "false".
470+
if len(values) == 0 {
471+
return "0 = 1"
472+
}
473+
460474
return c.Var(condBuilder{
461475
Builder: func(ctx *argsCompileContext) {
462476
ctx.WriteString(field)
@@ -475,6 +489,11 @@ func (c *Cond) All(field, op string, values ...interface{}) string {
475489
return ""
476490
}
477491

492+
// Empty values means "false".
493+
if len(values) == 0 {
494+
return "0 = 1"
495+
}
496+
478497
return c.Var(condBuilder{
479498
Builder: func(ctx *argsCompileContext) {
480499
ctx.WriteString(field)
@@ -493,6 +512,11 @@ func (c *Cond) Some(field, op string, values ...interface{}) string {
493512
return ""
494513
}
495514

515+
// Empty values means "false".
516+
if len(values) == 0 {
517+
return "0 = 1"
518+
}
519+
496520
return c.Var(condBuilder{
497521
Builder: func(ctx *argsCompileContext) {
498522
ctx.WriteString(field)

cond_test.go

Lines changed: 125 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -12,160 +12,162 @@ import (
1212

1313
type TestPair struct {
1414
Expected string
15-
Actual func(cond *Cond) string
15+
Actual string
16+
}
17+
18+
func newTestPair(expected string, fn func(c *Cond) string) *TestPair {
19+
cond := newCond()
20+
format := fn(cond)
21+
sql, _ := cond.Args.CompileWithFlavor(format, PostgreSQL)
22+
return &TestPair{
23+
Expected: expected,
24+
Actual: sql,
25+
}
1626
}
1727

1828
func TestCond(t *testing.T) {
1929
a := assert.New(t)
20-
cases := map[string]func(cond *Cond) string{
21-
"$a = $1": func(cond *Cond) string { return cond.Equal("$a", 123) },
22-
"$b = $1": func(cond *Cond) string { return cond.E("$b", 123) },
23-
"$c = $1": func(cond *Cond) string { return cond.EQ("$c", 123) },
24-
"$a <> $1": func(cond *Cond) string { return cond.NotEqual("$a", 123) },
25-
"$b <> $1": func(cond *Cond) string { return cond.NE("$b", 123) },
26-
"$c <> $1": func(cond *Cond) string { return cond.NEQ("$c", 123) },
27-
"$a > $1": func(cond *Cond) string { return cond.GreaterThan("$a", 123) },
28-
"$b > $1": func(cond *Cond) string { return cond.G("$b", 123) },
29-
"$c > $1": func(cond *Cond) string { return cond.GT("$c", 123) },
30-
"$a >= $1": func(cond *Cond) string { return cond.GreaterEqualThan("$a", 123) },
31-
"$b >= $1": func(cond *Cond) string { return cond.GE("$b", 123) },
32-
"$c >= $1": func(cond *Cond) string { return cond.GTE("$c", 123) },
33-
"$a < $1": func(cond *Cond) string { return cond.LessThan("$a", 123) },
34-
"$b < $1": func(cond *Cond) string { return cond.L("$b", 123) },
35-
"$c < $1": func(cond *Cond) string { return cond.LT("$c", 123) },
36-
"$a <= $1": func(cond *Cond) string { return cond.LessEqualThan("$a", 123) },
37-
"$b <= $1": func(cond *Cond) string { return cond.LE("$b", 123) },
38-
"$c <= $1": func(cond *Cond) string { return cond.LTE("$c", 123) },
39-
"$a IN ($1, $2, $3)": func(cond *Cond) string { return cond.In("$a", 1, 2, 3) },
40-
"$a NOT IN ($1, $2, $3)": func(cond *Cond) string { return cond.NotIn("$a", 1, 2, 3) },
41-
"$a LIKE $1": func(cond *Cond) string { return cond.Like("$a", "%Huan%") },
42-
"$a ILIKE $1": func(cond *Cond) string { return cond.ILike("$a", "%Huan%") },
43-
"$a NOT LIKE $1": func(cond *Cond) string { return cond.NotLike("$a", "%Huan%") },
44-
"$a NOT ILIKE $1": func(cond *Cond) string { return cond.NotILike("$a", "%Huan%") },
45-
"$a IS NULL": func(cond *Cond) string { return cond.IsNull("$a") },
46-
"$a IS NOT NULL": func(cond *Cond) string { return cond.IsNotNull("$a") },
47-
"$a BETWEEN $1 AND $2": func(cond *Cond) string { return cond.Between("$a", 123, 456) },
48-
"$a NOT BETWEEN $1 AND $2": func(cond *Cond) string { return cond.NotBetween("$a", 123, 456) },
49-
"NOT 1 = 1": func(cond *Cond) string { return cond.Not("1 = 1") },
50-
"EXISTS ($1)": func(cond *Cond) string { return cond.Exists(1) },
51-
"NOT EXISTS ($1)": func(cond *Cond) string { return cond.NotExists(1) },
52-
"$a > ANY ($1, $2)": func(cond *Cond) string { return cond.Any("$a", ">", 1, 2) },
53-
"$a < ALL ($1)": func(cond *Cond) string { return cond.All("$a", "<", 1) },
54-
"$a > SOME ($1, $2, $3)": func(cond *Cond) string { return cond.Some("$a", ">", 1, 2, 3) },
55-
"$a IS DISTINCT FROM $1": func(cond *Cond) string { return cond.IsDistinctFrom("$a", 1) },
56-
"$a IS NOT DISTINCT FROM $1": func(cond *Cond) string { return cond.IsNotDistinctFrom("$a", 1) },
57-
"$1": func(cond *Cond) string { return cond.Var(123) },
58-
}
59-
60-
for expected, f := range cases {
61-
actual := callCond(f)
62-
a.Equal(actual, expected)
30+
cases := []*TestPair{
31+
newTestPair("$a = $1", func(c *Cond) string { return c.Equal("$a", 123) }),
32+
newTestPair("$b = $1", func(c *Cond) string { return c.E("$b", 123) }),
33+
newTestPair("$c = $1", func(c *Cond) string { return c.EQ("$c", 123) }),
34+
newTestPair("$a <> $1", func(c *Cond) string { return c.NotEqual("$a", 123) }),
35+
newTestPair("$b <> $1", func(c *Cond) string { return c.NE("$b", 123) }),
36+
newTestPair("$c <> $1", func(c *Cond) string { return c.NEQ("$c", 123) }),
37+
newTestPair("$a > $1", func(c *Cond) string { return c.GreaterThan("$a", 123) }),
38+
newTestPair("$b > $1", func(c *Cond) string { return c.G("$b", 123) }),
39+
newTestPair("$c > $1", func(c *Cond) string { return c.GT("$c", 123) }),
40+
newTestPair("$a >= $1", func(c *Cond) string { return c.GreaterEqualThan("$a", 123) }),
41+
newTestPair("$b >= $1", func(c *Cond) string { return c.GE("$b", 123) }),
42+
newTestPair("$c >= $1", func(c *Cond) string { return c.GTE("$c", 123) }),
43+
newTestPair("$a < $1", func(c *Cond) string { return c.LessThan("$a", 123) }),
44+
newTestPair("$b < $1", func(c *Cond) string { return c.L("$b", 123) }),
45+
newTestPair("$c < $1", func(c *Cond) string { return c.LT("$c", 123) }),
46+
newTestPair("$a <= $1", func(c *Cond) string { return c.LessEqualThan("$a", 123) }),
47+
newTestPair("$b <= $1", func(c *Cond) string { return c.LE("$b", 123) }),
48+
newTestPair("$c <= $1", func(c *Cond) string { return c.LTE("$c", 123) }),
49+
newTestPair("$a IN ($1, $2, $3)", func(c *Cond) string { return c.In("$a", 1, 2, 3) }),
50+
newTestPair("0 = 1", func(c *Cond) string { return c.In("$a") }),
51+
newTestPair("$a NOT IN ($1, $2, $3)", func(c *Cond) string { return c.NotIn("$a", 1, 2, 3) }),
52+
newTestPair("$a LIKE $1", func(c *Cond) string { return c.Like("$a", "%Huan%") }),
53+
newTestPair("$a ILIKE $1", func(c *Cond) string { return c.ILike("$a", "%Huan%") }),
54+
newTestPair("$a NOT LIKE $1", func(c *Cond) string { return c.NotLike("$a", "%Huan%") }),
55+
newTestPair("$a NOT ILIKE $1", func(c *Cond) string { return c.NotILike("$a", "%Huan%") }),
56+
newTestPair("$a IS NULL", func(c *Cond) string { return c.IsNull("$a") }),
57+
newTestPair("$a IS NOT NULL", func(c *Cond) string { return c.IsNotNull("$a") }),
58+
newTestPair("$a BETWEEN $1 AND $2", func(c *Cond) string { return c.Between("$a", 123, 456) }),
59+
newTestPair("$a NOT BETWEEN $1 AND $2", func(c *Cond) string { return c.NotBetween("$a", 123, 456) }),
60+
newTestPair("NOT 1 = 1", func(c *Cond) string { return c.Not("1 = 1") }),
61+
newTestPair("EXISTS ($1)", func(c *Cond) string { return c.Exists(1) }),
62+
newTestPair("NOT EXISTS ($1)", func(c *Cond) string { return c.NotExists(1) }),
63+
newTestPair("$a > ANY ($1, $2)", func(c *Cond) string { return c.Any("$a", ">", 1, 2) }),
64+
newTestPair("0 = 1", func(c *Cond) string { return c.Any("$a", ">") }),
65+
newTestPair("$a < ALL ($1)", func(c *Cond) string { return c.All("$a", "<", 1) }),
66+
newTestPair("0 = 1", func(c *Cond) string { return c.All("$a", "<") }),
67+
newTestPair("$a > SOME ($1, $2, $3)", func(c *Cond) string { return c.Some("$a", ">", 1, 2, 3) }),
68+
newTestPair("0 = 1", func(c *Cond) string { return c.Some("$a", ">") }),
69+
newTestPair("$a IS DISTINCT FROM $1", func(c *Cond) string { return c.IsDistinctFrom("$a", 1) }),
70+
newTestPair("$a IS NOT DISTINCT FROM $1", func(c *Cond) string { return c.IsNotDistinctFrom("$a", 1) }),
71+
newTestPair("$1", func(c *Cond) string { return c.Var(123) }),
72+
}
73+
74+
for _, f := range cases {
75+
a.Equal(f.Actual, f.Expected)
6376
}
6477
}
6578

6679
func TestOrCond(t *testing.T) {
6780
a := assert.New(t)
68-
cases := []TestPair{
69-
{Expected: "(1 = 1 OR 2 = 2 OR 3 = 3)", Actual: func(cond *Cond) string { return cond.Or("1 = 1", "2 = 2", "3 = 3") }},
81+
cases := []*TestPair{
82+
newTestPair("(1 = 1 OR 2 = 2 OR 3 = 3)", func(c *Cond) string { return c.Or("1 = 1", "2 = 2", "3 = 3") }),
7083

71-
{Expected: "(1 = 1 OR 2 = 2)", Actual: func(cond *Cond) string { return cond.Or("", "1 = 1", "2 = 2") }},
72-
{Expected: "(1 = 1 OR 2 = 2)", Actual: func(cond *Cond) string { return cond.Or("1 = 1", "2 = 2", "") }},
73-
{Expected: "(1 = 1 OR 2 = 2)", Actual: func(cond *Cond) string { return cond.Or("1 = 1", "", "2 = 2") }},
84+
newTestPair("(1 = 1 OR 2 = 2)", func(c *Cond) string { return c.Or("", "1 = 1", "2 = 2") }),
85+
newTestPair("(1 = 1 OR 2 = 2)", func(c *Cond) string { return c.Or("1 = 1", "2 = 2", "") }),
86+
newTestPair("(1 = 1 OR 2 = 2)", func(c *Cond) string { return c.Or("1 = 1", "", "2 = 2") }),
7487

75-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.Or("1 = 1", "", "") }},
76-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.Or("", "1 = 1", "") }},
77-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.Or("", "", "1 = 1") }},
78-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.Or("1 = 1") }},
88+
newTestPair("(1 = 1)", func(c *Cond) string { return c.Or("1 = 1", "", "") }),
89+
newTestPair("(1 = 1)", func(c *Cond) string { return c.Or("", "1 = 1", "") }),
90+
newTestPair("(1 = 1)", func(c *Cond) string { return c.Or("", "", "1 = 1") }),
91+
newTestPair("(1 = 1)", func(c *Cond) string { return c.Or("1 = 1") }),
7992

80-
{Expected: "", Actual: func(cond *Cond) string { return cond.Or("") }},
81-
{Expected: "", Actual: func(cond *Cond) string { return cond.Or() }},
82-
{Expected: "", Actual: func(cond *Cond) string { return cond.Or("", "", "") }},
93+
{Expected: "", Actual: newCond().Or("")},
94+
{Expected: "", Actual: newCond().Or()},
95+
{Expected: "", Actual: newCond().Or("", "", "")},
8396
}
8497

8598
for _, f := range cases {
86-
actual := callCond(f.Actual)
87-
a.Equal(actual, f.Expected)
99+
a.Equal(f.Actual, f.Expected)
88100
}
89101
}
90102

91103
func TestAndCond(t *testing.T) {
92104
a := assert.New(t)
93-
cases := []TestPair{
94-
{Expected: "(1 = 1 AND 2 = 2 AND 3 = 3)", Actual: func(cond *Cond) string { return cond.And("1 = 1", "2 = 2", "3 = 3") }},
105+
cases := []*TestPair{
106+
newTestPair("(1 = 1 AND 2 = 2 AND 3 = 3)", func(c *Cond) string { return c.And("1 = 1", "2 = 2", "3 = 3") }),
95107

96-
{Expected: "(1 = 1 AND 2 = 2)", Actual: func(cond *Cond) string { return cond.And("", "1 = 1", "2 = 2") }},
97-
{Expected: "(1 = 1 AND 2 = 2)", Actual: func(cond *Cond) string { return cond.And("1 = 1", "2 = 2", "") }},
98-
{Expected: "(1 = 1 AND 2 = 2)", Actual: func(cond *Cond) string { return cond.And("1 = 1", "", "2 = 2") }},
108+
newTestPair("(1 = 1 AND 2 = 2)", func(c *Cond) string { return c.And("", "1 = 1", "2 = 2") }),
109+
newTestPair("(1 = 1 AND 2 = 2)", func(c *Cond) string { return c.And("1 = 1", "2 = 2", "") }),
110+
newTestPair("(1 = 1 AND 2 = 2)", func(c *Cond) string { return c.And("1 = 1", "", "2 = 2") }),
99111

100-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.And("1 = 1", "", "") }},
101-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.And("", "1 = 1", "") }},
102-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.And("", "", "1 = 1") }},
103-
{Expected: "(1 = 1)", Actual: func(cond *Cond) string { return cond.And("1 = 1") }},
112+
newTestPair("(1 = 1)", func(c *Cond) string { return c.And("1 = 1", "", "") }),
113+
newTestPair("(1 = 1)", func(c *Cond) string { return c.And("", "1 = 1", "") }),
114+
newTestPair("(1 = 1)", func(c *Cond) string { return c.And("", "", "1 = 1") }),
115+
newTestPair("(1 = 1)", func(c *Cond) string { return c.And("1 = 1") }),
104116

105-
{Expected: "", Actual: func(cond *Cond) string { return cond.And("") }},
106-
{Expected: "", Actual: func(cond *Cond) string { return cond.And() }},
107-
{Expected: "", Actual: func(cond *Cond) string { return cond.And("", "", "") }},
117+
{Expected: "", Actual: newCond().And("")},
118+
{Expected: "", Actual: newCond().And()},
119+
{Expected: "", Actual: newCond().And("", "", "")},
108120
}
109121

110122
for _, f := range cases {
111-
actual := callCond(f.Actual)
112-
a.Equal(actual, f.Expected)
123+
a.Equal(f.Actual, f.Expected)
113124
}
114125
}
115126

116127
func TestEmptyCond(t *testing.T) {
117128
a := assert.New(t)
118-
cases := []func(cond *Cond) string{
119-
func(cond *Cond) string { return cond.Equal("", 123) },
120-
func(cond *Cond) string { return cond.NotEqual("", 123) },
121-
func(cond *Cond) string { return cond.GreaterThan("", 123) },
122-
func(cond *Cond) string { return cond.GreaterEqualThan("", 123) },
123-
func(cond *Cond) string { return cond.LessThan("", 123) },
124-
func(cond *Cond) string { return cond.LessEqualThan("", 123) },
125-
func(cond *Cond) string { return cond.In("", 1, 2, 3) },
126-
func(cond *Cond) string { return cond.NotIn("", 1, 2, 3) },
127-
func(cond *Cond) string { return cond.Like("", "%Huan%") },
128-
func(cond *Cond) string { return cond.ILike("", "%Huan%") },
129-
func(cond *Cond) string { return cond.NotLike("", "%Huan%") },
130-
func(cond *Cond) string { return cond.NotILike("", "%Huan%") },
131-
func(cond *Cond) string { return cond.IsNull("") },
132-
func(cond *Cond) string { return cond.IsNotNull("") },
133-
func(cond *Cond) string { return cond.Between("", 123, 456) },
134-
func(cond *Cond) string { return cond.NotBetween("", 123, 456) },
135-
func(cond *Cond) string { return cond.Not("") },
136-
137-
func(cond *Cond) string { return cond.Any("", "", 1, 2) },
138-
func(cond *Cond) string { return cond.Any("", ">", 1, 2) },
139-
func(cond *Cond) string { return cond.Any("$a", "", 1, 2) },
140-
141-
func(cond *Cond) string { return cond.All("", "", 1) },
142-
func(cond *Cond) string { return cond.All("", ">", 1) },
143-
func(cond *Cond) string { return cond.All("$a", "", 1) },
144-
145-
func(cond *Cond) string { return cond.Some("", "", 1, 2, 3) },
146-
func(cond *Cond) string { return cond.Some("", ">", 1, 2, 3) },
147-
func(cond *Cond) string { return cond.Some("$a", "", 1, 2, 3) },
148-
149-
func(cond *Cond) string { return cond.IsDistinctFrom("", 1) },
150-
func(cond *Cond) string { return cond.IsNotDistinctFrom("", 1) },
129+
cases := []string{
130+
newCond().Equal("", 123),
131+
newCond().NotEqual("", 123),
132+
newCond().GreaterThan("", 123),
133+
newCond().GreaterEqualThan("", 123),
134+
newCond().LessThan("", 123),
135+
newCond().LessEqualThan("", 123),
136+
newCond().In("", 1, 2, 3),
137+
newCond().NotIn("", 1, 2, 3),
138+
newCond().NotIn("a"),
139+
newCond().Like("", "%Huan%"),
140+
newCond().ILike("", "%Huan%"),
141+
newCond().NotLike("", "%Huan%"),
142+
newCond().NotILike("", "%Huan%"),
143+
newCond().IsNull(""),
144+
newCond().IsNotNull(""),
145+
newCond().Between("", 123, 456),
146+
newCond().NotBetween("", 123, 456),
147+
newCond().Not(""),
148+
149+
newCond().Any("", "", 1, 2),
150+
newCond().Any("", ">", 1, 2),
151+
newCond().Any("$a", "", 1, 2),
152+
153+
newCond().All("", "", 1),
154+
newCond().All("", ">", 1),
155+
newCond().All("$a", "", 1),
156+
157+
newCond().Some("", "", 1, 2, 3),
158+
newCond().Some("", ">", 1, 2, 3),
159+
newCond().Some("$a", "", 1, 2, 3),
160+
161+
newCond().IsDistinctFrom("", 1),
162+
newCond().IsNotDistinctFrom("", 1),
151163
}
152164

153165
expected := ""
154-
for _, f := range cases {
155-
actual := callCond(f)
166+
for _, actual := range cases {
156167
a.Equal(actual, expected)
157168
}
158169
}
159170

160-
func callCond(fn func(cond *Cond) string) (actual string) {
161-
cond := &Cond{
162-
Args: &Args{},
163-
}
164-
format := fn(cond)
165-
actual, _ = cond.Args.CompileWithFlavor(format, PostgreSQL)
166-
return
167-
}
168-
169171
func TestCondWithFlavor(t *testing.T) {
170172
a := assert.New(t)
171173
cond := &Cond{
@@ -243,3 +245,10 @@ func TestCondMisuse(t *testing.T) {
243245
a.Equal(sql, "SELECT * FROM t1 WHERE /* INVALID ARG $256 */")
244246
a.Equal(args, nil)
245247
}
248+
249+
func newCond() *Cond {
250+
args := &Args{}
251+
return &Cond{
252+
Args: args,
253+
}
254+
}

delete.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,6 @@ func (db *DeleteBuilder) Build() (sql string, args []interface{}) {
172172
// BuildWithFlavor returns compiled DELETE string and args with flavor and initial args.
173173
// They can be used in `DB#Query` of package `database/sql` directly.
174174
func (db *DeleteBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
175-
176175
buf := newStringBuilder()
177176
db.injection.WriteTo(buf, deleteMarkerInit)
178177

select.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -390,9 +390,9 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
390390
buf.WriteLeadingString("JOIN ")
391391
buf.WriteString(sb.joinTables[i])
392392

393-
if exprs := sb.joinExprs[i]; len(exprs) > 0 {
393+
if exprs := filterEmptyStrings(sb.joinExprs[i]); len(exprs) > 0 {
394394
buf.WriteString(" ON ")
395-
buf.WriteStrings(sb.joinExprs[i], " AND ")
395+
buf.WriteStrings(exprs, " AND ")
396396
}
397397
}
398398

@@ -414,9 +414,9 @@ func (sb *SelectBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{
414414
buf.WriteLeadingString("GROUP BY ")
415415
buf.WriteStrings(sb.groupByCols, ", ")
416416

417-
if len(sb.havingExprs) > 0 {
417+
if havingExprs := filterEmptyStrings(sb.havingExprs); len(havingExprs) > 0 {
418418
buf.WriteString(" HAVING ")
419-
buf.WriteStrings(sb.havingExprs, " AND ")
419+
buf.WriteStrings(havingExprs, " AND ")
420420
}
421421

422422
sb.injection.WriteTo(buf, selectMarkerAfterGroupBy)

0 commit comments

Comments
 (0)