-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsqlbuilder_orderby.go
145 lines (117 loc) · 3.51 KB
/
sqlbuilder_orderby.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package sqle
import (
"strings"
)
// OrderByBuilder represents a SQL ORDER BY clause builder.
// It is used to construct ORDER BY clauses for SQL queries.
type OrderByBuilder struct {
*Builder // The underlying SQL query builder.
written bool // Indicates if the ORDER BY clause has been written.
options *BuilderOptions // The list of allowed columns for ordering.
}
// NewOrderBy creates a new instance of the OrderByBuilder.
// It takes a variadic parameter `allowedColumns` which specifies the columns that are allowed to be used in the ORDER BY clause.
func NewOrderBy(opts ...BuilderOption) *OrderByBuilder {
ob := &OrderByBuilder{
Builder: New(),
options: &BuilderOptions{},
}
for _, o := range opts {
o(ob.options)
}
return ob
}
// WithOrderBy sets the order by clause for the SQL query.
// It takes an instance of the OrderByBuilder and adds the allowed columns to the Builder's order list.
// It also appends the SQL string representation of the OrderByBuilder to the Builder's SQL string.
// It returns a new instance of the OrderByBuilder.
func (b *Builder) WithOrderBy(ob *OrderByBuilder) *OrderByBuilder {
if ob == nil {
return nil
}
n := b.Order()
b.SQL(ob.String())
return n
}
// Order create an OrderByBuilder with allowed columns to prevent sql injection. NB: any input is allowed if it is not provided
func (b *Builder) Order(opts ...BuilderOption) *OrderByBuilder {
ob := &OrderByBuilder{
Builder: b,
options: &BuilderOptions{},
}
for _, o := range opts {
o(ob.options)
}
return ob
}
// isAllowed check if column is included in allowed columns. It will remove any untrust input from client
func (ob *OrderByBuilder) getColumn(col string) (string, bool) {
if ob.options.Columns == nil {
return col, true
}
if ob.options.ToName != nil {
col = ob.options.ToName(col)
}
for _, c := range ob.options.Columns {
if strings.EqualFold(c, col) {
return c, true
}
}
return "", false
}
// By order by raw sql. eg By("a asc, b desc")
func (ob *OrderByBuilder) By(raw string) *OrderByBuilder {
cols := strings.Split(raw, ",")
var n int
var items []string
var by string
for _, col := range cols {
items = strings.Split(strings.TrimSpace(col), " ")
n = len(items)
switch n {
case 1:
ob.ByAsc(strings.TrimSpace(col))
case 2:
by = strings.TrimSpace(items[1])
if strings.EqualFold(by, "ASC") {
ob.ByAsc(strings.TrimSpace(items[0]))
} else if strings.EqualFold(by, "DESC") {
ob.ByDesc(strings.TrimSpace(items[0]))
}
}
}
return ob
}
// ByAsc order by ascending with columns
func (ob *OrderByBuilder) ByAsc(columns ...string) *OrderByBuilder {
for _, c := range columns {
ob.add(c, " ASC")
}
return ob
}
// ByDesc order by descending with columns
func (ob *OrderByBuilder) ByDesc(columns ...string) *OrderByBuilder {
for _, c := range columns {
ob.add(c, " DESC")
}
return ob
}
// add adds a column and its sorting direction to the OrderByBuilder.
// It checks if the column is allowed and appends it to the SQL query.
// If the column has already been written, it appends a comma before adding the column.
// If it's the first column being added, it appends "ORDER BY" before adding the column.
func (ob *OrderByBuilder) add(col, direction string) {
c, ok := ob.getColumn(col)
if ok {
if ob.written {
ob.Builder.SQL(", ").SQL(ob.quoteColumn(c)).SQL(direction)
} else {
// only write once
if !ob.written {
ob.Builder.SQL(" ORDER BY ")
}
ob.Builder.SQL(ob.quoteColumn(c)).SQL(direction)
ob.written = true
}
}
}