-
Notifications
You must be signed in to change notification settings - Fork 1
/
sqlc.go
196 lines (172 loc) · 5.56 KB
/
sqlc.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Package sqlc helps you combine arbitrary bits of SQL
package sqlc
import (
"fmt"
"strings"
)
type component struct {
partial string
args []interface{}
}
/*
From the psql documentation:
SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ]
* | expression [ [ AS ] output_name ] [, ...]
[ FROM from_item [, ...] ]
[ WHERE condition ]
[ GROUP BY expression [, ...] ]
[ HAVING condition [, ...] ]
[ WINDOW window_name AS ( window_definition ) [, ...] ]
[ { UNION | INTERSECT | EXCEPT } [ ALL | DISTINCT ] select ]
[ ORDER BY expression [ ASC | DESC | USING operator ] [ NULLS { FIRST | LAST } ] [, ...] ]
[ LIMIT { count | ALL } ]
[ OFFSET start [ ROW | ROWS ] ]
[ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
[ FOR { UPDATE | NO KEY UPDATE | SHARE | KEY SHARE } [ OF table_name [, ...] ] [ NOWAIT ] [...] ]
From the mysql documentation:
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr ...]
[FROM table_references
[PARTITION partition_list]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[INTO OUTFILE 'file_name'
[CHARACTER SET charset_name]
export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name]]
[FOR UPDATE | LOCK IN SHARE MODE]]
*/
// Statement is a SQL string being built
type Statement struct {
// PostgreSQL replaces "?, ?" with "$1, $2" if true
PostgreSQL bool
selects []component
froms []component
joins []component
wheres []component
groups []component
havings []component
orders []component
limit component
}
// Select adds a SELECT stanza, joined by commas
func (s Statement) Select(partial string, args ...interface{}) Statement {
s.selects = append(s.selects, component{partial, args})
return s
}
// From adds a FROM stanza, joined by commas
func (s Statement) From(partial string, args ...interface{}) Statement {
s.froms = append(s.froms, component{partial, args})
return s
}
// Join adds a JOIN stanza, joined by spaces
// Unlike other stanzas, you need to specify the JOIN/INNER JOIN/LEFT JOIN bit yourself.
func (s Statement) Join(partial string, args ...interface{}) Statement {
s.joins = append(s.joins, component{partial, args})
return s
}
// Where adds a WHERE stanza, wrapped in brackets and joined by AND
func (s Statement) Where(partial string, args ...interface{}) Statement {
s.wheres = append(s.wheres, component{"(" + partial + ")", args})
return s
}
// Having adds a HAVING stanza, wrapped in brackets and joined by AND
func (s Statement) Having(partial string, args ...interface{}) Statement {
s.havings = append(s.havings, component{"(" + partial + ")", args})
return s
}
// Group adds a GROUP BY stanza, joined by commas
func (s Statement) Group(partial string, args ...interface{}) Statement {
s.groups = append(s.groups, component{partial, args})
return s
}
// Order adds an ORDER BY stanza, joined by commas
func (s Statement) Order(partial string, args ...interface{}) Statement {
s.orders = append(s.orders, component{partial, args})
return s
}
// Limit sets or overwrites the LIMIT stanza
func (s Statement) Limit(partial string, args ...interface{}) Statement {
s.limit = component{partial, args}
return s
}
// Args returns positional arguments in the order they will appear in the SQL.
func (s Statement) Args() []interface{} {
args := make([]interface{}, 0)
appendArgs(&args, s.selects...)
appendArgs(&args, s.froms...)
appendArgs(&args, s.joins...)
appendArgs(&args, s.wheres...)
appendArgs(&args, s.groups...)
appendArgs(&args, s.havings...)
appendArgs(&args, s.orders...)
appendArgs(&args, s.limit)
return args
}
// SQL joins your stanzas, returning the composed SQL.
func (s Statement) SQL() string {
parts := make([]string, 0)
if len(s.selects) > 0 {
parts = append(parts, "SELECT "+joinParts(s.selects, ", "))
}
if len(s.froms) > 0 {
parts = append(parts, "FROM "+joinParts(s.froms, " ,"))
}
if len(s.joins) > 0 {
parts = append(parts, joinParts(s.joins, " "))
}
if len(s.wheres) > 0 {
parts = append(parts, "WHERE "+joinParts(s.wheres, " AND "))
}
if len(s.groups) > 0 {
parts = append(parts, "GROUP BY "+joinParts(s.groups, " ,"))
}
if len(s.havings) > 0 {
parts = append(parts, "HAVING "+joinParts(s.havings, " AND "))
}
if len(s.orders) > 0 {
parts = append(parts, "ORDER BY "+joinParts(s.orders, " ,"))
}
if s.limit.partial != "" {
parts = append(parts, "LIMIT "+s.limit.partial)
}
sql := strings.Join(parts, "\n")
if s.PostgreSQL {
sql = replacePositionalArguments(sql, 1)
}
return sql
}
func joinParts(components []component, joiner string) string {
partials := make([]string, 0)
for _, component := range components {
partials = append(partials, component.partial)
}
return strings.Join(partials, joiner)
}
func appendArgs(args *[]interface{}, components ...component) {
for _, component := range components {
if len(component.args) > 0 {
*args = append(*args, component.args...)
}
}
}
func replacePositionalArguments(sql string, c int) string {
arg := fmt.Sprintf("$%d", c)
newSql := strings.Replace(sql, "?", arg, 1)
if newSql != sql {
return replacePositionalArguments(newSql, c+1)
}
return newSql
}