forked from ido50/sqlz
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwith.go
147 lines (124 loc) · 4.72 KB
/
with.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
package sqlz
import (
"context"
"database/sql"
"strings"
"github.com/jmoiron/sqlx"
)
// AuxStmt represents an auxiliary statement that is part
// of a WITH query. It includes the statement itself, and
// the name used for referencing it in other queries
type AuxStmt struct {
Stmt SQLStmt
As string
}
// WithStmt represents a WITH statement
type WithStmt struct {
// AuxStmts is the list of auxiliary statements that are
// part of the WITH query
AuxStmts []AuxStmt
// MainStmt is the query's main statement in which the
// auxiliary statements can be referenced
MainStmt SQLStmt
execer Ext
}
// With creates a new WithStmt object including
// the provided auxiliary statements
func (db *DB) With(stmt SQLStmt, as string) *WithStmt {
return &WithStmt{
AuxStmts: []AuxStmt{{stmt, as}},
execer: db.DB,
}
}
// With creates a new WithStmt object including
// the provided auxiliary statements
func (tx *Tx) With(stmt SQLStmt, as string) *WithStmt {
return &WithStmt{
AuxStmts: []AuxStmt{{stmt, as}},
execer: tx.Tx,
}
}
// And adds another auxiliary statement to the query
func (stmt *WithStmt) And(auxStmt SQLStmt, as string) *WithStmt {
stmt.AuxStmts = append(stmt.AuxStmts, AuxStmt{auxStmt, as})
return stmt
}
// Then sets the main statement of the WITH query
func (stmt *WithStmt) Then(mainStmt SQLStmt) *WithStmt {
stmt.MainStmt = mainStmt
return stmt
}
// ToSQL generates the WITH statement's SQL and returns a list of
// bindings. It is used internally by Exec, GetRow and GetAll, but is
// exported if you wish to use it directly.
func (stmt *WithStmt) ToSQL(rebind bool) (asSQL string, bindings []interface{}) {
var clauses = []string{"WITH"}
auxStmts := make([]string, len(stmt.AuxStmts))
for i, aux := range stmt.AuxStmts {
auxSQL, auxBindings := aux.Stmt.ToSQL(false)
bindings = append(bindings, auxBindings...)
auxStmts[i] = aux.As + " AS (" + auxSQL + ")"
}
clauses = append(clauses, strings.Join(auxStmts, ", "))
mainSQL, mainBindings := stmt.MainStmt.ToSQL(false)
clauses = append(clauses, mainSQL)
bindings = append(bindings, mainBindings...)
asSQL = strings.Join(clauses, " ")
if db, ok := stmt.execer.(*sqlx.DB); ok {
asSQL = db.Rebind(asSQL)
} else if tx, ok := stmt.execer.(*sqlx.Tx); ok {
asSQL = tx.Rebind(asSQL)
}
return asSQL, bindings
}
// Exec executes the WITH statement, returning the standard
// sql.Result struct and an error if the query failed.
func (stmt *WithStmt) Exec() (res sql.Result, err error) {
asSQL, bindings := stmt.ToSQL(true)
return stmt.execer.Exec(asSQL, bindings...)
}
// ExecContext executes the WITH statement, returning the standard
// sql.Result struct and an error if the query failed.
func (stmt *WithStmt) ExecContext(ctx context.Context) (res sql.Result, err error) {
asSQL, bindings := stmt.ToSQL(true)
return stmt.execer.ExecContext(ctx, asSQL, bindings...)
}
// GetRow executes a WITH statement whose main statement has
// a RETURNING clause expected to return one row, and loads
// the result into the provided variable (which may be a
// simple variable if only one column is returned, or a
// struct if multiple columns are returned)
func (stmt *WithStmt) GetRow(into interface{}) error {
asSQL, bindings := stmt.ToSQL(true)
return sqlx.Get(stmt.execer, into, asSQL, bindings...)
}
// GetRowContext executes a WITH statement whose main statement has
// a RETURNING clause expected to return one row, and loads
// the result into the provided variable (which may be a
// simple variable if only one column is returned, or a
// struct if multiple columns are returned)
func (stmt *WithStmt) GetRowContext(ctx context.Context, into interface{}) error {
asSQL, bindings := stmt.ToSQL(true)
return sqlx.GetContext(ctx, stmt.execer, into, asSQL, bindings...)
}
// GetAll executes a WITH statement whose main statement has
// a RETURNING clause expected to return multiple rows, and
// loads the result into the provided slice variable
func (stmt *WithStmt) GetAll(into interface{}) error {
asSQL, bindings := stmt.ToSQL(true)
return sqlx.Select(stmt.execer, into, asSQL, bindings...)
}
// GetAllContext executes a WITH statement whose main statement has
// a RETURNING clause expected to return multiple rows, and
// loads the result into the provided slice variable
func (stmt *WithStmt) GetAllContext(ctx context.Context, into interface{}) error {
asSQL, bindings := stmt.ToSQL(true)
return sqlx.SelectContext(ctx, stmt.execer, into, asSQL, bindings...)
}
// GetAllAsRows executes the WITH statement and returns an sqlx.Rows object
// to use for iteration. It is the caller's responsibility to close the cursor
// with Close().
func (stmt *WithStmt) GetAllAsRows() (rows *sqlx.Rows, err error) {
asSQL, bindings := stmt.ToSQL(true)
return stmt.execer.Queryx(asSQL, bindings...)
}