-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathupdate.go
107 lines (92 loc) · 2.75 KB
/
update.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
package sol
import (
"fmt"
"strings"
"github.com/aodin/sol/dialect"
)
// UpdateStmt is the internal representation of an SQL UPDATE statement.
type UpdateStmt struct {
ConditionalStmt
table *TableElem
values Values
}
// String outputs the parameter-less UPDATE statement in a neutral dialect.
func (stmt UpdateStmt) String() string {
compiled, _ := stmt.Compile(&defaultDialect{}, Params())
return compiled
}
// Compile outputs the UPDATE statement using the given dialect and parameters.
func (stmt UpdateStmt) Compile(d dialect.Dialect, ps *Parameters) (string, error) {
// Return immediately if there are existing errors
if err := stmt.Error(); err != nil {
return "", err
}
// If no values were attached, then create a default values map
if stmt.values == nil {
stmt.values = Values{}
for _, column := range stmt.table.Columns() {
stmt.values[column.Name()] = nil
}
}
// Error if there are no values
if len(stmt.values) == 0 {
return "", fmt.Errorf("sol: UPDATE has no values")
}
// Compile the values
compiledValues, err := stmt.values.Compile(d, ps)
if err != nil {
return "", fmt.Errorf("sol: failed to compile UPDATE values: %s", err)
}
// Being building the statement
compiled := []string{UPDATE, stmt.table.Name(), SET, compiledValues}
// Add a conditional statement if it exists
if stmt.where != nil {
cc, err := stmt.where.Compile(d, ps)
if err != nil {
return "", err
}
compiled = append(compiled, WHERE, cc)
}
return strings.Join(compiled, WHITESPACE), nil
}
// Values attaches the given values to the statement. The keys of values
// must match columns in the table.
// TODO Allow structs to be used if a primary key is specified in the schema
func (stmt UpdateStmt) Values(values Values) UpdateStmt {
// There must be some columns to update!
if len(values) == 0 {
stmt.AddMeta("sol: there must be at least one value to update")
return stmt
}
// Confirm that all values' keys are columns in the table
// TODO perform column alias matching? e.g. UUID > uuid or ItemID > item_id
for key := range values {
if !stmt.table.Has(key) {
stmt.AddMeta(
"sol: no column '%s' exists in the table '%s'",
key, stmt.table.Name(),
)
}
}
stmt.values = values
return stmt
}
// Where adds a conditional WHERE clause to the UPDATE statement.
func (stmt UpdateStmt) Where(clauses ...Clause) UpdateStmt {
if len(clauses) > 1 {
// By default, multiple where clauses will be joined will AllOf
stmt.where = AllOf(clauses...)
} else if len(clauses) == 1 {
stmt.where = clauses[0]
}
return stmt
}
// Update creates an UPDATE statement for the given table.
func Update(table *TableElem) (stmt UpdateStmt) {
if table == nil {
stmt.AddMeta("sol: attempting to UPDATE a nil table")
return
}
stmt.table = table
return
}