Skip to content

Commit d72e331

Browse files
authored
fix(sqlbuilder): fixed WithWhere/WithOrderBy for empty builder (#39)
1 parent 665c28e commit d72e331

File tree

4 files changed

+71
-22
lines changed

4 files changed

+71
-22
lines changed

Diff for: sqlbuilder_orderby.go

+27-20
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import (
55
"strings"
66
)
77

8+
// OrderByBuilder represents a SQL ORDER BY clause builder.
9+
// It is used to construct ORDER BY clauses for SQL queries.
810
type OrderByBuilder struct {
9-
*Builder
10-
isWritten bool
11-
allowedColumns []string
11+
*Builder // The underlying SQL query builder.
12+
written bool // Indicates if the ORDER BY clause has been written.
13+
allowedColumns []string // The list of allowed columns for ordering.
1214
}
1315

1416
// NewOrderBy creates a new instance of the OrderByBuilder.
@@ -43,8 +45,6 @@ func (b *Builder) Order(allowedColumns ...string) *OrderByBuilder {
4345
allowedColumns: allowedColumns,
4446
}
4547

46-
b.SQL(" ORDER BY ")
47-
4848
return ob
4949
}
5050

@@ -89,29 +89,36 @@ func (ob *OrderByBuilder) By(raw string) *OrderByBuilder {
8989
// ByAsc order by ascending with columns
9090
func (ob *OrderByBuilder) ByAsc(columns ...string) *OrderByBuilder {
9191
for _, c := range columns {
92-
if ob.isAllowed(c) {
93-
if ob.isWritten {
94-
ob.Builder.SQL(", ").SQL(c).SQL(" ASC")
95-
} else {
96-
ob.Builder.SQL(c).SQL(" ASC")
97-
ob.isWritten = true
98-
}
99-
}
92+
ob.add(c, " ASC")
10093
}
10194
return ob
10295
}
10396

10497
// ByDesc order by descending with columns
10598
func (ob *OrderByBuilder) ByDesc(columns ...string) *OrderByBuilder {
10699
for _, c := range columns {
107-
if ob.isAllowed(c) {
108-
if ob.isWritten {
109-
ob.Builder.SQL(", ").SQL(c).SQL(" DESC")
110-
} else {
111-
ob.Builder.SQL(c).SQL(" DESC")
112-
ob.isWritten = true
100+
ob.add(c, " DESC")
101+
}
102+
return ob
103+
}
104+
105+
// add adds a column and its sorting direction to the OrderByBuilder.
106+
// It checks if the column is allowed and appends it to the SQL query.
107+
// If the column has already been written, it appends a comma before adding the column.
108+
// If it's the first column being added, it appends "ORDER BY" before adding the column.
109+
func (ob *OrderByBuilder) add(col, direction string) {
110+
if ob.isAllowed(col) {
111+
if ob.written {
112+
ob.Builder.SQL(", ").SQL(col).SQL(direction)
113+
} else {
114+
// only write once
115+
if !ob.written {
116+
ob.Builder.SQL(" ORDER BY ")
113117
}
118+
119+
ob.Builder.SQL(col).SQL(direction)
120+
121+
ob.written = true
114122
}
115123
}
116-
return ob
117124
}

Diff for: sqlbuilder_orderby_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ func TestOrderByBuilder(t *testing.T) {
7878
},
7979
wanted: "SELECT * FROM users ORDER BY id ASC, created_at DESC, updated_at ASC",
8080
},
81+
{
82+
name: "with_empty_order_by_should_work",
83+
build: func() *Builder {
84+
b := New("SELECT * FROM users")
85+
86+
ob := NewOrderBy("age").
87+
ByAsc("id", "name").
88+
ByDesc("created_at", "unsafe_input").
89+
ByAsc("updated_at")
90+
91+
b.WithOrderBy(ob)
92+
93+
return b
94+
},
95+
wanted: "SELECT * FROM users",
96+
},
8197
}
8298

8399
for _, test := range tests {

Diff for: sqlbuilder_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,26 @@ func TestBuilder(t *testing.T) {
207207
require.Equal(t, now, vars[2])
208208
},
209209
},
210+
{
211+
name: "build_with_empty_where",
212+
build: func() *Builder {
213+
b := New().Select("orders")
214+
b.Where().
215+
If(false).SQL("AND", "cancelled>={now}").
216+
If(false).SQL("AND", "id={order_id}")
217+
b.Param("order_id", 123456)
218+
b.Param("now", now)
219+
220+
b.WithWhere(nil)
221+
return b
222+
},
223+
assert: func(t *testing.T, b *Builder) {
224+
s, vars, err := b.Build()
225+
require.NoError(t, err)
226+
require.Equal(t, "SELECT * FROM `orders`", s)
227+
require.Len(t, vars, 0)
228+
},
229+
},
210230
{
211231
name: "build_update",
212232
build: func() *Builder {

Diff for: sqlbuilder_where.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,14 @@ func NewWhere() *WhereBuilder {
2424
func (b *Builder) Where(criteria ...string) *WhereBuilder {
2525
wb := &WhereBuilder{Builder: b}
2626

27-
b.stmt.WriteString(" WHERE")
2827
for _, it := range criteria {
2928
if it != "" {
29+
30+
if !wb.written {
31+
b.stmt.WriteString(" WHERE")
32+
}
3033
wb.written = true
34+
3135
b.stmt.WriteString(" ")
3236
b.stmt.WriteString(it)
3337
}
@@ -52,7 +56,7 @@ func (b *Builder) WithWhere(wb *WhereBuilder) *WhereBuilder {
5256
b.Param(k, v)
5357
}
5458

55-
return b.Where(strings.TrimSpace(wb.stmt.String()))
59+
return b.Where(strings.TrimPrefix(wb.stmt.String(), " WHERE "))
5660
}
5761

5862
// If sets a condition to skip the subsequent SQL statements.
@@ -84,6 +88,8 @@ func (wb *WhereBuilder) SQL(op string, criteria string) *WhereBuilder {
8488
if wb.written {
8589
wb.Builder.stmt.WriteString(" ")
8690
wb.Builder.stmt.WriteString(op)
91+
} else {
92+
wb.Builder.stmt.WriteString(" WHERE")
8793
}
8894

8995
wb.written = true

0 commit comments

Comments
 (0)