Skip to content

Commit

Permalink
reduce count of memory allocations in Cond.And/Or/Not
Browse files Browse the repository at this point in the history
  • Loading branch information
huandu committed Sep 24, 2024
1 parent e114413 commit 5d9be7f
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 7 deletions.
53 changes: 46 additions & 7 deletions cond.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

package sqlbuilder

const (
lparen = "("
rparen = ")"
opOR = " OR "
opAND = " AND "
opNOT = "NOT "
)

// Cond provides several helper methods to build conditions.
type Cond struct {
Args *Args
Expand Down Expand Up @@ -319,26 +327,49 @@ func (c *Cond) NotBetween(field string, lower, upper interface{}) string {

// Or is used to construct the expression OR logic like "expr1 OR expr2 OR expr3".
func (c *Cond) Or(orExpr ...string) string {
if len(orExpr) == 0 {
return ""
}

buf := newStringBuilder()
buf.WriteString("(")
buf.WriteStrings(orExpr, " OR ")
buf.WriteString(")")

// Ensure that there is only 1 memory allocation.
size := len(lparen) + len(rparen) + (len(orExpr)-1)*len(opOR) + estimateStringsBytes(orExpr)
buf.Grow(size)

buf.WriteString(lparen)
buf.WriteStrings(orExpr, opOR)
buf.WriteString(rparen)
return buf.String()
}

// And is used to construct the expression AND logic like "expr1 AND expr2 AND expr3".
func (c *Cond) And(andExpr ...string) string {
if len(andExpr) == 0 {
return ""
}

buf := newStringBuilder()
buf.WriteString("(")
buf.WriteStrings(andExpr, " AND ")
buf.WriteString(")")

// Ensure that there is only 1 memory allocation.
size := len(lparen) + len(rparen) + (len(andExpr)-1)*len(opAND) + estimateStringsBytes(andExpr)
buf.Grow(size)

buf.WriteString(lparen)
buf.WriteStrings(andExpr, opAND)
buf.WriteString(rparen)
return buf.String()
}

// Not is used to construct the expression "NOT expr".
func (c *Cond) Not(notExpr string) string {
buf := newStringBuilder()
buf.WriteString("NOT ")

// Ensure that there is only 1 memory allocation.
size := len(opNOT) + len(notExpr)
buf.Grow(size)

buf.WriteString(opNOT)
buf.WriteString(notExpr)
return buf.String()
}
Expand Down Expand Up @@ -514,3 +545,11 @@ func (c *Cond) Var(value interface{}) string {
type condBuilder struct {
Builder func(ctx *argsCompileContext)
}

func estimateStringsBytes(strs []string) (n int) {
for _, s := range strs {
n += len(s)
}

return
}
29 changes: 29 additions & 0 deletions cond_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,32 @@ CASE WHEN f4 IS NULL AND ? IS NULL THEN 1 WHEN f4 IS NOT NULL AND ? IS NOT NULL
a.Equal(actual, expected)
}
}

func TestCondExpr(t *testing.T) {
a := assert.New(t)
cond := &Cond{
Args: &Args{},
}
sb1 := Select("1 = 1")
sb2 := Select("FALSE")
formats := []string{
cond.And(),
cond.Or(),
cond.And(cond.Var(sb1), cond.Var(sb2)),
cond.Or(cond.Var(sb1), cond.Var(sb2)),
cond.Not(cond.Or(cond.Var(sb1), cond.And(cond.Var(sb1), cond.Var(sb2)))),
}
expectResults := []string{
"",
"",
"(SELECT 1 = 1 AND SELECT FALSE)",
"(SELECT 1 = 1 OR SELECT FALSE)",
"NOT (SELECT 1 = 1 OR (SELECT 1 = 1 AND SELECT FALSE))",
}

for i, expected := range expectResults {
actual, values := cond.Args.Compile(formats[i])
a.Equal(len(values), 0)
a.Equal(actual, expected)
}
}
4 changes: 4 additions & 0 deletions stringbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ func (sb *stringBuilder) String() string {
func (sb *stringBuilder) Reset() {
sb.builder.Reset()
}

func (sb *stringBuilder) Grow(n int) {
sb.builder.Grow(n)
}

0 comments on commit 5d9be7f

Please sign in to comment.