forked from Masterminds/squirrel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
expr.go
165 lines (141 loc) · 3.51 KB
/
expr.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
package squirrel
import (
"database/sql/driver"
"fmt"
"io"
"reflect"
"strings"
)
type expr struct {
sql string
args []interface{}
}
// Expr builds value expressions for InsertBuilder and UpdateBuilder.
//
// Ex:
// .Values(Expr("FROM_UNIXTIME(?)", t))
func Expr(sql string, args ...interface{}) expr {
return expr{sql: sql, args: args}
}
func (e expr) ToSql() (sql string, args []interface{}, err error) {
return e.sql, e.args, nil
}
type exprs []expr
func (es exprs) AppendToSql(w io.Writer, sep string, args []interface{}) ([]interface{}, error) {
for i, e := range es {
if i > 0 {
_, err := io.WriteString(w, sep)
if err != nil {
return nil, err
}
}
_, err := io.WriteString(w, e.sql)
if err != nil {
return nil, err
}
args = append(args, e.args...)
}
return args, nil
}
// aliasExpr helps to alias part of SQL query generated with underlying "expr"
type aliasExpr struct {
expr Sqlizer
alias string
}
// Alias allows to define alias for column in SelectBuilder. Useful when column is
// defined as complex expression like IF or CASE
// Ex:
// .Column(Alias(caseStmt, "case_column"))
func Alias(expr Sqlizer, alias string) aliasExpr {
return aliasExpr{expr, alias}
}
func (e aliasExpr) ToSql() (sql string, args []interface{}, err error) {
sql, args, err = e.expr.ToSql()
if err == nil {
sql = fmt.Sprintf("(%s) AS %s", sql, e.alias)
}
return
}
// Eq is syntactic sugar for use with Where/Having/Set methods.
// Ex:
// .Where(Eq{"id": 1})
type Eq map[string]interface{}
func (eq Eq) toSql(useNotOpr bool) (sql string, args []interface{}, err error) {
var (
exprs []string
equalOpr string = "="
inOpr string = "IN"
nullOpr string = "IS"
)
if useNotOpr {
equalOpr = "<>"
inOpr = "NOT IN"
nullOpr = "IS NOT"
}
for key, val := range eq {
expr := ""
switch v := val.(type) {
case driver.Valuer:
if val, err = v.Value(); err != nil {
return
}
}
if val == nil {
expr = fmt.Sprintf("%s %s NULL", key, nullOpr)
} else {
valVal := reflect.ValueOf(val)
if valVal.Kind() == reflect.Array || valVal.Kind() == reflect.Slice {
if valVal.Len() == 0 {
err = fmt.Errorf("equality condition must contain at least one paramater")
return
}
for i := 0; i < valVal.Len(); i++ {
args = append(args, valVal.Index(i).Interface())
}
expr = fmt.Sprintf("%s %s (%s)", key, inOpr, Placeholders(valVal.Len()))
} else {
expr = fmt.Sprintf("%s %s ?", key, equalOpr)
args = append(args, val)
}
}
exprs = append(exprs, expr)
}
sql = strings.Join(exprs, " AND ")
return
}
func (eq Eq) ToSql() (sql string, args []interface{}, err error) {
return eq.toSql(false)
}
// NotEq is syntactic sugar for use with Where/Having/Set methods.
// Ex:
// .Where(NotEq{"id": 1}) == "id <> 1"
type NotEq Eq
func (neq NotEq) ToSql() (sql string, args []interface{}, err error) {
return Eq(neq).toSql(true)
}
type conj []Sqlizer
func (c conj) join(sep string) (sql string, args []interface{}, err error) {
var sqlParts []string
for _, sqlizer := range c {
partSql, partArgs, err := sqlizer.ToSql()
if err != nil {
return "", nil, err
}
if partSql != "" {
sqlParts = append(sqlParts, partSql)
args = append(args, partArgs...)
}
}
if len(sqlParts) > 0 {
sql = fmt.Sprintf("(%s)", strings.Join(sqlParts, sep))
}
return
}
type And conj
func (a And) ToSql() (string, []interface{}, error) {
return conj(a).join(" AND ")
}
type Or conj
func (o Or) ToSql() (string, []interface{}, error) {
return conj(o).join(" OR ")
}