-
Notifications
You must be signed in to change notification settings - Fork 123
/
cte.go
167 lines (133 loc) · 4.49 KB
/
cte.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
// Copyright 2024 Huan Du. All rights reserved.
// Licensed under the MIT license that can be found in the LICENSE file.
package sqlbuilder
const (
cteMarkerInit injectionMarker = iota
cteMarkerAfterWith
)
// With creates a new CTE builder with default flavor.
func With(tables ...*CTEQueryBuilder) *CTEBuilder {
return DefaultFlavor.NewCTEBuilder().With(tables...)
}
// WithRecursive creates a new recursive CTE builder with default flavor.
func WithRecursive(tables ...*CTEQueryBuilder) *CTEBuilder {
return DefaultFlavor.NewCTEBuilder().WithRecursive(tables...)
}
func newCTEBuilder() *CTEBuilder {
return &CTEBuilder{
args: &Args{},
injection: newInjection(),
}
}
// CTEBuilder is a CTE (Common Table Expression) builder.
type CTEBuilder struct {
recursive bool
queries []*CTEQueryBuilder
queryBuilderVars []string
args *Args
injection *injection
marker injectionMarker
}
var _ Builder = new(CTEBuilder)
// With sets the CTE name and columns.
func (cteb *CTEBuilder) With(queries ...*CTEQueryBuilder) *CTEBuilder {
queryBuilderVars := make([]string, 0, len(queries))
for _, query := range queries {
queryBuilderVars = append(queryBuilderVars, cteb.args.Add(query))
}
cteb.queries = queries
cteb.queryBuilderVars = queryBuilderVars
cteb.marker = cteMarkerAfterWith
return cteb
}
// WithRecursive sets the CTE name and columns and turns on the RECURSIVE keyword.
func (cteb *CTEBuilder) WithRecursive(queries ...*CTEQueryBuilder) *CTEBuilder {
cteb.With(queries...).recursive = true
return cteb
}
// Select creates a new SelectBuilder to build a SELECT statement using this CTE.
func (cteb *CTEBuilder) Select(col ...string) *SelectBuilder {
sb := cteb.args.Flavor.NewSelectBuilder()
return sb.With(cteb).Select(col...)
}
// DeleteFrom creates a new DeleteBuilder to build a DELETE statement using this CTE.
func (cteb *CTEBuilder) DeleteFrom(table string) *DeleteBuilder {
db := cteb.args.Flavor.NewDeleteBuilder()
return db.With(cteb).DeleteFrom(table)
}
// Update creates a new UpdateBuilder to build an UPDATE statement using this CTE.
func (cteb *CTEBuilder) Update(table string) *UpdateBuilder {
ub := cteb.args.Flavor.NewUpdateBuilder()
return ub.With(cteb).Update(table)
}
// String returns the compiled CTE string.
func (cteb *CTEBuilder) String() string {
sql, _ := cteb.Build()
return sql
}
// Build returns compiled CTE string and args.
func (cteb *CTEBuilder) Build() (sql string, args []interface{}) {
return cteb.BuildWithFlavor(cteb.args.Flavor)
}
// BuildWithFlavor builds a CTE with the specified flavor and initial arguments.
func (cteb *CTEBuilder) BuildWithFlavor(flavor Flavor, initialArg ...interface{}) (sql string, args []interface{}) {
buf := newStringBuilder()
cteb.injection.WriteTo(buf, cteMarkerInit)
if len(cteb.queryBuilderVars) > 0 {
buf.WriteLeadingString("WITH ")
if cteb.recursive {
buf.WriteString("RECURSIVE ")
}
buf.WriteStrings(cteb.queryBuilderVars, ", ")
}
cteb.injection.WriteTo(buf, cteMarkerAfterWith)
return cteb.args.CompileWithFlavor(buf.String(), flavor, initialArg...)
}
// SetFlavor sets the flavor of compiled sql.
func (cteb *CTEBuilder) SetFlavor(flavor Flavor) (old Flavor) {
old = cteb.args.Flavor
cteb.args.Flavor = flavor
return
}
// Flavor returns flavor of builder
func (cteb *CTEBuilder) Flavor() Flavor {
return cteb.args.Flavor
}
// SQL adds an arbitrary sql to current position.
func (cteb *CTEBuilder) SQL(sql string) *CTEBuilder {
cteb.injection.SQL(cteb.marker, sql)
return cteb
}
// TableNames returns all table names in a CTE.
func (cteb *CTEBuilder) TableNames() []string {
if len(cteb.queryBuilderVars) == 0 {
return nil
}
tableNames := make([]string, 0, len(cteb.queries))
for _, query := range cteb.queries {
tableNames = append(tableNames, query.TableName())
}
return tableNames
}
// tableNamesForFrom returns a list of table names which should be automatically added to FROM clause.
// It's not public, as this feature is designed only for SelectBuilder/UpdateBuilder/DeleteBuilder right now.
func (cteb *CTEBuilder) tableNamesForFrom() []string {
cnt := 0
// ShouldAddToTableList() unlikely returns true.
// Count it before allocating any memory for better performance.
for _, query := range cteb.queries {
if query.ShouldAddToTableList() {
cnt++
}
}
if cnt == 0 {
return nil
}
tableNames := make([]string, 0, cnt)
for _, query := range cteb.queries {
if query.ShouldAddToTableList() {
tableNames = append(tableNames, query.TableName())
}
}
return tableNames
}