-
Notifications
You must be signed in to change notification settings - Fork 2
/
expression.go
196 lines (173 loc) · 5.24 KB
/
expression.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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package qb
import (
"fmt"
"strings"
)
const (
// SQLNow is the SQL NOW() function for use as a value in expressions.
SQLNow = "NOW()"
// SQLNull is the SQL representation of NULL
SQLNull = "NULL"
)
type expressionUnion struct {
value interface{}
field *TableField
multi []expressionUnion
}
func newUnion(values ...interface{}) expressionUnion {
if len(values) == 1 {
tf, ok := values[0].(TableField)
if ok {
return expressionUnion{field: &tf}
}
return expressionUnion{value: values[0]}
}
multi := make([]expressionUnion, len(values))
for i, obj := range values {
multi[i] = newUnion(obj)
}
return expressionUnion{multi: multi}
}
func (union expressionUnion) isString() bool {
_, ok := union.value.(string)
return ok
}
func (union expressionUnion) isField() bool {
return nil != union.field
}
func (union expressionUnion) isMulti() bool {
return nil != union.multi
}
func (union expressionUnion) getTables() []string {
if union.isField() {
return union.field.GetTables()
} else if union.isMulti() {
tables := []string{}
for _, exp := range union.multi {
tables = append(tables, exp.getTables()...)
}
return tables
} else {
return []string{}
}
}
func (union expressionUnion) sql() (string, []interface{}) {
var sql string
values := []interface{}{}
if union.isMulti() {
sa := make([]string, len(union.multi))
var subvalues []interface{}
for i, exp := range union.multi {
sa[i], subvalues = exp.sql()
values = append(values, subvalues...)
}
sql = "(" + strings.Join(sa, ", ") + ")"
} else if union.isField() {
sql = union.field.SQL()
} else if SQLNow == union.value || SQLNull == union.value {
sql = fmt.Sprintf("%s", union.value)
} else if union.isString() && strings.HasPrefix(union.value.(string), ":") {
sql = fmt.Sprintf("%s", union.value)
} else {
sql = "?"
values = append(values, union.value)
}
return sql, values
}
type comparisonExpression interface {
SQL() (string, []interface{})
}
type parameterExpression struct {
left TableField
comparison Comparison
}
func (be parameterExpression) SQL() (string, []interface{}) {
left := be.left.SQL()
return fmt.Sprintf("%s %s :%s", left, be.comparison, be.left.GetName()), make([]interface{}, 0)
}
type binaryExpression struct {
left TableField
comparison Comparison
right expressionUnion
}
func (be binaryExpression) SQL() (string, []interface{}) {
left := be.left.SQL()
right, values := be.right.sql()
return fmt.Sprintf("%s %s %s", left, be.comparison, right), values
}
// ConditionExpression represents an expression that can be used as a condition in a where or join on.
type ConditionExpression struct {
binary *binaryExpression
left *ConditionExpression
operator string
right *ConditionExpression
}
// Tables that are used in this expression or it's sub expressions.
func (exp *ConditionExpression) Tables() []string {
tables := []string{}
if nil != exp.binary {
tables = append(tables, exp.binary.left.GetTables()...)
tables = append(tables, exp.binary.right.getTables()...)
} else {
tables = append(tables, exp.left.Tables()...)
tables = append(tables, exp.right.Tables()...)
}
return tables
}
// FieldComparison to another field or a discrete value.
func FieldComparison(left TableField, comparison Comparison, right interface{}) *ConditionExpression {
if nil == right {
right = SQLNull
}
return &ConditionExpression{binary: &binaryExpression{left: left, comparison: comparison, right: newUnion(right)}}
}
// FieldIn a series of TableFields and/or values
func FieldIn(left TableField, in ...interface{}) *ConditionExpression {
// swap any 'nils' for sql null
rightValues := make([]interface{}, len(in))
for i, value := range in {
if nil == value {
value = SQLNull
}
rightValues[i] = value
}
comparison := In
if len(rightValues) == 1 {
comparison = Equal
}
return &ConditionExpression{binary: &binaryExpression{left: left, comparison: comparison, right: newUnion(rightValues...)}}
}
// And creates an expression with this and the passed expression with an AND conjunction.
func (exp *ConditionExpression) And(expression *ConditionExpression) *ConditionExpression {
ptr := &ConditionExpression{}
*ptr = *exp
wrap := &ConditionExpression{left: ptr, right: expression, operator: And}
*exp = *wrap
return exp
}
// Or creates an expression with this and the passed expression with an OR conjunction.
func (exp *ConditionExpression) Or(expression *ConditionExpression) *ConditionExpression {
ptr := &ConditionExpression{}
*ptr = *exp
wrap := &ConditionExpression{left: ptr, right: expression, operator: Or}
*exp = *wrap
return exp
}
// XOr creates an expression with this and the passed expression with an XOr conjunction.
func (exp *ConditionExpression) XOr(expression *ConditionExpression) *ConditionExpression {
ptr := &ConditionExpression{}
*ptr = *exp
wrap := &ConditionExpression{left: ptr, right: expression, operator: XOr}
*exp = *wrap
return exp
}
// SQL returns this condition expression as a SQL expression.
func (exp *ConditionExpression) SQL() (string, []interface{}) {
if nil != exp.binary {
return exp.binary.SQL()
}
lsql, values := exp.left.SQL()
rsql, rvalues := exp.right.SQL()
values = append(values, rvalues...)
return fmt.Sprintf("(%s %s %s)", lsql, exp.operator, rsql), values
}