-
Notifications
You must be signed in to change notification settings - Fork 27
/
update_statement.go
175 lines (150 loc) · 4.94 KB
/
update_statement.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
package godb
import "github.com/samonzeweb/godb/adapters"
// UpdateStatement will contains all parts needed to build an UPDATE statement.
// Initialize it with the UpdateTable method.
//
// Example :
// count, err := db.UpdateTable("bar").
// Set("foo", 1).
// Where("foo = ?", 2).
// Do()
type UpdateStatement struct {
db *DB
updateTable string
sets []*setPart
where []*Condition
returningColumns []string
suffixes []string
}
// setPart contains elements for a single SET clause.
// The value could be nil for a raw clause (ie count=count+1)
type setPart struct {
// The column name, or the full SET clause for a raw clause
column string
// The value, or nil if it's a raw clause
value interface{}
}
// UpdateTable creates an UpdateStatement and specify table to update.
// It's the entry point to build an UPDATE query.
func (db *DB) UpdateTable(tableName string) *UpdateStatement {
us := &UpdateStatement{db: db}
us.updateTable = tableName
return us
}
// Set adds a part of SET clause to the query.
func (us *UpdateStatement) Set(column string, value interface{}) *UpdateStatement {
setClause := &setPart{
column: column,
value: value,
}
us.sets = append(us.sets, setClause)
return us
}
// SetRaw adds a raw SET clause to the query.
func (us *UpdateStatement) SetRaw(rawSQL string) *UpdateStatement {
rawSetClause := &setPart{
column: rawSQL,
value: nil,
}
us.sets = append(us.sets, rawSetClause)
return us
}
// Where adds a condition using string and arguments.
func (us *UpdateStatement) Where(sql string, args ...interface{}) *UpdateStatement {
return us.WhereQ(Q(sql, args...))
}
// WhereQ adds a simple or complex predicate generated with Q and
// confunctions.
func (us *UpdateStatement) WhereQ(condition *Condition) *UpdateStatement {
us.where = append(us.where, condition)
return us
}
// Returning adds a RETURNING or OUTPUT clause to the statement. Use it with
// PostgreSQL and SQL Server.
func (us *UpdateStatement) Returning(columns ...string) *UpdateStatement {
us.returningColumns = append(us.returningColumns, columns...)
return us
}
// Suffix adds an expression to suffix the statement. Use it to add a
// RETURNING clause with PostgreSQL (or whatever you need).
func (us *UpdateStatement) Suffix(suffix string) *UpdateStatement {
us.suffixes = append(us.suffixes, suffix)
return us
}
// approximateSetLength returns an approximation of final size of all set
// clauses.
func (us *UpdateStatement) approximateSetLength() int {
// initialized with the count needed for "=" and ","
length := 2 * len(us.sets)
for _, s := range us.sets {
// column or raw sql
length += len(s.column)
if s.value != nil {
stringValue, isString := s.value.(string)
if isString {
length += len(stringValue)
} else {
// arbitrary
length += 2
}
}
}
return length
}
// ToSQL returns a string with the SQL statement (containing placeholders),
// the arguments slices, and an error.
func (us *UpdateStatement) ToSQL() (string, []interface{}, error) {
sqlWhereLength, argsWhereLength, err := sumOfConditionsLengths(us.where)
if err != nil {
return "", nil, err
}
sqlBuffer := newSQLBuffer(
us.db.adapter,
sqlWhereLength+us.approximateSetLength()+64,
argsWhereLength,
)
sqlBuffer.Write("UPDATE ")
sqlBuffer.Write(us.updateTable)
sqlBuffer.writeSets(us.sets).
writeReturningForPosition(us.returningColumns, adapters.ReturningSQLServer).
writeWhere(us.where).
writeReturningForPosition(us.returningColumns, adapters.ReturningPostgreSQL).
writeStringsWithSpaces(us.suffixes)
return sqlBuffer.SQL(), sqlBuffer.Arguments(), sqlBuffer.Err()
}
// Do executes the builded query, and return RowsAffected()
func (us *UpdateStatement) Do() (int64, error) {
query, args, err := us.ToSQL()
if err != nil {
return 0, err
}
result, err := us.db.do(query, args)
if err != nil {
return 0, err
}
rowsAffected, err := result.RowsAffected()
return rowsAffected, err
}
// DoWithReturning executes the statement and fills the fields according to
// the columns in RETURNING clause.
func (us *UpdateStatement) DoWithReturning(record interface{}) (int64, error) {
recordDescription, err := buildRecordDescription(record)
if err != nil {
return 0, err
}
// the function which will return the pointers according to the given columns
f := func(record interface{}, columns []string) ([]interface{}, error) {
pointers, err := recordDescription.structMapping.GetPointersForColumns(record, columns...)
return pointers, err
}
return us.doWithReturning(recordDescription, f)
}
// DoWithReturning executes the statement and fills the fields according to
// the columns in RETURNING clause. It returns the count of rows returned.
func (us *UpdateStatement) doWithReturning(recordDescription *recordDescription, pointersGetter pointersGetter) (int64, error) {
query, args, err := us.ToSQL()
if err != nil {
return 0, err
}
return us.db.doSelectOrWithReturning(query, args, recordDescription, pointersGetter)
}