Skip to content

Commit e346582

Browse files
committed
add cte support and settings support
1 parent 1ded578 commit e346582

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

cte.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package squirrel
2+
3+
import (
4+
"bytes"
5+
"strings"
6+
)
7+
8+
// CTE represents a single common table expression. They are composed of an alias, a few optional components, and a data manipulation statement, though exactly what sort of statement depends on the database system you're using. MySQL, for example, only allows SELECT statements; others, like PostgreSQL, permit INSERTs, UPDATEs, and DELETEs.
9+
// The optional components supported by this fork of Squirrel include:
10+
// * a list of columns
11+
// * the keyword RECURSIVE, the use of which may place additional constraints on the data manipulation statement
12+
type CTE struct {
13+
Alias string
14+
ColumnList []string
15+
Recursive bool
16+
Expression Sqlizer
17+
}
18+
19+
// ToSql builds the SQL for a CTE
20+
func (c CTE) ToSql() (string, []interface{}, error) {
21+
var buf bytes.Buffer
22+
if c.Recursive {
23+
buf.WriteString("RECURSIVE ")
24+
}
25+
26+
buf.WriteString(c.Alias)
27+
if len(c.ColumnList) > 0 {
28+
buf.WriteString("(")
29+
buf.WriteString(strings.Join(c.ColumnList, ", "))
30+
buf.WriteString(")")
31+
}
32+
33+
buf.WriteString(" AS (")
34+
sql, args, err := c.Expression.ToSql()
35+
if err != nil {
36+
return "", []interface{}{}, err
37+
}
38+
buf.WriteString(sql)
39+
buf.WriteString(")")
40+
41+
return buf.String(), args, nil
42+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/Masterminds/squirrel
1+
module github.com/zyreio/squirrel
22

33
go 1.14
44

select.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ type selectData struct {
1313
PlaceholderFormat PlaceholderFormat
1414
RunWith BaseRunner
1515
Prefixes []Sqlizer
16+
CTEs []Sqlizer
1617
Options []string
1718
Columns []Sqlizer
1819
From Sqlizer
1920
Joins []Sqlizer
2021
WhereParts []Sqlizer
2122
GroupBys []string
23+
Settings []string
2224
HavingParts []Sqlizer
2325
OrderByParts []Sqlizer
2426
Limit string
@@ -78,6 +80,15 @@ func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) {
7880
sql.WriteString(" ")
7981
}
8082

83+
if len(d.CTEs) > 0 {
84+
sql.WriteString("WITH ")
85+
args, err = appendToSql(d.CTEs, sql, ", ", args)
86+
if err != nil {
87+
return
88+
}
89+
sql.WriteString(" ")
90+
}
91+
8192
sql.WriteString("SELECT ")
8293

8394
if len(d.Options) > 0 {
@@ -147,6 +158,11 @@ func (d *selectData) toSqlRaw() (sqlStr string, args []interface{}, err error) {
147158
sql.WriteString(d.Offset)
148159
}
149160

161+
if len(d.Settings) > 0 {
162+
sql.WriteString(" SETTINGS ")
163+
sql.WriteString(strings.Join(d.Settings, ", "))
164+
}
165+
150166
if len(d.Suffixes) > 0 {
151167
sql.WriteString(" ")
152168

@@ -253,6 +269,22 @@ func (b SelectBuilder) Options(options ...string) SelectBuilder {
253269
return builder.Extend(b, "Options", options).(SelectBuilder)
254270
}
255271

272+
// With adds a non-recursive CTE to the query.
273+
func (b SelectBuilder) With(alias string, expr Sqlizer) SelectBuilder {
274+
return b.WithCTE(CTE{Alias: alias, ColumnList: []string{}, Recursive: false, Expression: expr})
275+
}
276+
277+
// WithRecursive adds a recursive CTE to the query.
278+
func (b SelectBuilder) WithRecursive(alias string, expr Sqlizer) SelectBuilder {
279+
return b.WithCTE(CTE{Alias: alias, ColumnList: []string{}, Recursive: true, Expression: expr})
280+
}
281+
282+
// WithCTE adds an arbitrary Sqlizer to the query.
283+
// The sqlizer will be sandwiched between the keyword WITH and, if there's more than one CTE, a comma.
284+
func (b SelectBuilder) WithCTE(cte Sqlizer) SelectBuilder {
285+
return builder.Append(b, "CTEs", cte).(SelectBuilder)
286+
}
287+
256288
// Columns adds result columns to the query.
257289
func (b SelectBuilder) Columns(columns ...string) SelectBuilder {
258290
parts := make([]interface{}, 0, len(columns))
@@ -272,7 +304,8 @@ func (b SelectBuilder) RemoveColumns() SelectBuilder {
272304
// Column adds a result column to the query.
273305
// Unlike Columns, Column accepts args which will be bound to placeholders in
274306
// the columns string, for example:
275-
// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3)
307+
//
308+
// Column("IF(col IN ("+squirrel.Placeholders(3)+"), 1, 0) as col", 1, 2, 3)
276309
func (b SelectBuilder) Column(column interface{}, args ...interface{}) SelectBuilder {
277310
return builder.Append(b, "Columns", newPart(column, args...)).(SelectBuilder)
278311
}
@@ -351,6 +384,11 @@ func (b SelectBuilder) GroupBy(groupBys ...string) SelectBuilder {
351384
return builder.Extend(b, "GroupBys", groupBys).(SelectBuilder)
352385
}
353386

387+
// Setting adds SETTINGS expressions to the query.
388+
func (b SelectBuilder) Setting(settings ...string) SelectBuilder {
389+
return builder.Extend(b, "Settings", settings).(SelectBuilder)
390+
}
391+
354392
// Having adds an expression to the HAVING clause of the query.
355393
//
356394
// See Where.

0 commit comments

Comments
 (0)