Skip to content

Commit 5d9be7f

Browse files
committed
reduce count of memory allocations in Cond.And/Or/Not
1 parent e114413 commit 5d9be7f

File tree

3 files changed

+79
-7
lines changed

3 files changed

+79
-7
lines changed

cond.go

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33

44
package sqlbuilder
55

6+
const (
7+
lparen = "("
8+
rparen = ")"
9+
opOR = " OR "
10+
opAND = " AND "
11+
opNOT = "NOT "
12+
)
13+
614
// Cond provides several helper methods to build conditions.
715
type Cond struct {
816
Args *Args
@@ -319,26 +327,49 @@ func (c *Cond) NotBetween(field string, lower, upper interface{}) string {
319327

320328
// Or is used to construct the expression OR logic like "expr1 OR expr2 OR expr3".
321329
func (c *Cond) Or(orExpr ...string) string {
330+
if len(orExpr) == 0 {
331+
return ""
332+
}
333+
322334
buf := newStringBuilder()
323-
buf.WriteString("(")
324-
buf.WriteStrings(orExpr, " OR ")
325-
buf.WriteString(")")
335+
336+
// Ensure that there is only 1 memory allocation.
337+
size := len(lparen) + len(rparen) + (len(orExpr)-1)*len(opOR) + estimateStringsBytes(orExpr)
338+
buf.Grow(size)
339+
340+
buf.WriteString(lparen)
341+
buf.WriteStrings(orExpr, opOR)
342+
buf.WriteString(rparen)
326343
return buf.String()
327344
}
328345

329346
// And is used to construct the expression AND logic like "expr1 AND expr2 AND expr3".
330347
func (c *Cond) And(andExpr ...string) string {
348+
if len(andExpr) == 0 {
349+
return ""
350+
}
351+
331352
buf := newStringBuilder()
332-
buf.WriteString("(")
333-
buf.WriteStrings(andExpr, " AND ")
334-
buf.WriteString(")")
353+
354+
// Ensure that there is only 1 memory allocation.
355+
size := len(lparen) + len(rparen) + (len(andExpr)-1)*len(opAND) + estimateStringsBytes(andExpr)
356+
buf.Grow(size)
357+
358+
buf.WriteString(lparen)
359+
buf.WriteStrings(andExpr, opAND)
360+
buf.WriteString(rparen)
335361
return buf.String()
336362
}
337363

338364
// Not is used to construct the expression "NOT expr".
339365
func (c *Cond) Not(notExpr string) string {
340366
buf := newStringBuilder()
341-
buf.WriteString("NOT ")
367+
368+
// Ensure that there is only 1 memory allocation.
369+
size := len(opNOT) + len(notExpr)
370+
buf.Grow(size)
371+
372+
buf.WriteString(opNOT)
342373
buf.WriteString(notExpr)
343374
return buf.String()
344375
}
@@ -514,3 +545,11 @@ func (c *Cond) Var(value interface{}) string {
514545
type condBuilder struct {
515546
Builder func(ctx *argsCompileContext)
516547
}
548+
549+
func estimateStringsBytes(strs []string) (n int) {
550+
for _, s := range strs {
551+
n += len(s)
552+
}
553+
554+
return
555+
}

cond_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,32 @@ CASE WHEN f4 IS NULL AND ? IS NULL THEN 1 WHEN f4 IS NOT NULL AND ? IS NOT NULL
104104
a.Equal(actual, expected)
105105
}
106106
}
107+
108+
func TestCondExpr(t *testing.T) {
109+
a := assert.New(t)
110+
cond := &Cond{
111+
Args: &Args{},
112+
}
113+
sb1 := Select("1 = 1")
114+
sb2 := Select("FALSE")
115+
formats := []string{
116+
cond.And(),
117+
cond.Or(),
118+
cond.And(cond.Var(sb1), cond.Var(sb2)),
119+
cond.Or(cond.Var(sb1), cond.Var(sb2)),
120+
cond.Not(cond.Or(cond.Var(sb1), cond.And(cond.Var(sb1), cond.Var(sb2)))),
121+
}
122+
expectResults := []string{
123+
"",
124+
"",
125+
"(SELECT 1 = 1 AND SELECT FALSE)",
126+
"(SELECT 1 = 1 OR SELECT FALSE)",
127+
"NOT (SELECT 1 = 1 OR (SELECT 1 = 1 AND SELECT FALSE))",
128+
}
129+
130+
for i, expected := range expectResults {
131+
actual, values := cond.Args.Compile(formats[i])
132+
a.Equal(len(values), 0)
133+
a.Equal(actual, expected)
134+
}
135+
}

stringbuilder.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,7 @@ func (sb *stringBuilder) String() string {
6262
func (sb *stringBuilder) Reset() {
6363
sb.builder.Reset()
6464
}
65+
66+
func (sb *stringBuilder) Grow(n int) {
67+
sb.builder.Grow(n)
68+
}

0 commit comments

Comments
 (0)