@@ -6,6 +6,15 @@ import (
6
6
"strings"
7
7
)
8
8
9
+ const (
10
+ DB_MYSQL DatabaseType = iota
11
+ DB_POSTGRESQL
12
+ )
13
+
14
+ type DatabaseType int
15
+
16
+ var DB DatabaseType = DB_MYSQL
17
+
9
18
type Filter struct {
10
19
Glue string `json:"glue"`
11
20
Field string `json:"field"`
@@ -17,22 +26,30 @@ type Filter struct {
17
26
type CustomOperation func (string , string , []interface {}) (string , []interface {}, error )
18
27
19
28
type SQLConfig struct {
20
- Whitelist map [string ]bool
21
- Operations map [string ]CustomOperation
29
+ Whitelist map [string ]bool
30
+ Operations map [string ]CustomOperation
31
+ DynamicFields []DynamicField
32
+ DynamicConfigName string
33
+ }
34
+
35
+ type DynamicField struct {
36
+ Key string `json:"key"`
37
+ Type string `json:"type"`
22
38
}
23
39
24
40
func FromJSON (text []byte ) (Filter , error ) {
25
41
f := Filter {}
26
42
err := json .Unmarshal (text , & f )
43
+
27
44
return f , err
28
45
}
29
46
30
47
var NoValues = make ([]interface {}, 0 )
31
48
32
- func inSQL (field string , data []interface {}) (string , []interface {}, error ) {
49
+ func inSQL (field string , data []interface {}, placeholder string ) (string , []interface {}, error ) {
33
50
marks := make ([]string , len (data ))
34
51
for i := range marks {
35
- marks [i ] = "?"
52
+ marks [i ] = placeholder
36
53
}
37
54
38
55
sql := fmt .Sprintf ("%s IN(%s)" , field , strings .Join (marks , "," ))
@@ -45,65 +62,151 @@ func GetSQL(data Filter, config *SQLConfig) (string, []interface{}, error) {
45
62
return "" , nil , fmt .Errorf ("field name is not in whitelist: %s" , data .Field )
46
63
}
47
64
65
+ ph , err := getPlaceholder ()
66
+ if err != nil {
67
+ return "" , nil , err
68
+ }
69
+
70
+ var isDynamicField bool
71
+ if DB == DB_POSTGRESQL {
72
+ f := getDynamicField (config .DynamicFields , data .Field )
73
+ if f != nil {
74
+ if config .DynamicConfigName == "" {
75
+ return "" , nil , fmt .Errorf ("dynamic config name is empty" )
76
+ }
77
+ parts := strings .Split (data .Field , "." )
78
+ tp := GetJSONBType (f .Type )
79
+ var s , e string
80
+ if tp == "date" {
81
+ s = "CAST("
82
+ e = " AS DATE)"
83
+ tp = "text"
84
+ }
85
+ if len (parts ) == 1 {
86
+ data .Field = fmt .Sprintf ("%s(%s->'%s')::%s%s" , s , config .DynamicConfigName , parts [0 ], tp , e )
87
+ } else if len (parts ) == 2 {
88
+ data .Field = fmt .Sprintf ("%s(\" %s\" .%s->'%s')::%s%s" , s , parts [0 ], config .DynamicConfigName , parts [1 ], tp , e )
89
+ }
90
+ isDynamicField = true
91
+ }
92
+ }
93
+
48
94
if len (data .Includes ) > 0 {
49
- return inSQL (data .Field , data .Includes )
95
+ return inSQL (data .Field , data .Includes , ph )
50
96
}
51
97
52
98
values := data .Condition .getValues ()
53
99
switch data .Condition .Rule {
54
100
case "" :
55
101
return "" , NoValues , nil
56
102
case "equal" :
57
- return fmt .Sprintf ("%s = ? " , data .Field ), values , nil
103
+ return fmt .Sprintf ("%s = %s " , data .Field , ph ), values , nil
58
104
case "notEqual" :
59
- return fmt .Sprintf ("%s <> ? " , data .Field ), values , nil
105
+ return fmt .Sprintf ("%s <> %s " , data .Field , ph ), values , nil
60
106
case "contains" :
61
- return fmt .Sprintf ("INSTR(%s, ?) > 0" , data .Field ), values , nil
107
+ switch DB {
108
+ case DB_MYSQL :
109
+ return fmt .Sprintf ("INSTR(%s, ?) > 0" , data .Field ), values , nil
110
+ case DB_POSTGRESQL :
111
+ if isDynamicField {
112
+ // Quotes (" ... ") are needed for correct work. Fields of type text in JSONB are wrapped by default
113
+ return fmt .Sprintf ("%s LIKE '\" %%' || $ || '%%\" '" , data .Field ), values , nil
114
+ }
115
+ return fmt .Sprintf ("%s LIKE '%%' || $ || '%%'" , data .Field ), values , nil
116
+ }
62
117
case "notContains" :
63
- return fmt .Sprintf ("INSTR(%s, ?) = 0" , data .Field ), values , nil
118
+ switch DB {
119
+ case DB_MYSQL :
120
+ return fmt .Sprintf ("INSTR(%s, ?) = 0" , data .Field ), values , nil
121
+ case DB_POSTGRESQL :
122
+ if isDynamicField {
123
+ return fmt .Sprintf ("%s NOT LIKE '\" %%' || $ || '%%\" '" , data .Field ), values , nil
124
+ }
125
+ return fmt .Sprintf ("%s NOT LIKE '%%' || $ || '%%'" , data .Field ), values , nil
126
+ }
64
127
case "lessOrEqual" :
65
- return fmt .Sprintf ("%s <= ? " , data .Field ), values , nil
128
+ return fmt .Sprintf ("%s <= %s " , data .Field , ph ), values , nil
66
129
case "greaterOrEqual" :
67
- return fmt .Sprintf ("%s >= ? " , data .Field ), values , nil
130
+ return fmt .Sprintf ("%s >= %s " , data .Field , ph ), values , nil
68
131
case "less" :
69
- return fmt .Sprintf ("%s < ? " , data .Field ), values , nil
132
+ return fmt .Sprintf ("%s < %s " , data .Field , ph ), values , nil
70
133
case "notBetween" :
71
134
if len (values ) != 2 {
72
135
return "" , nil , fmt .Errorf ("wrong number of parameters for notBetween operation: %d" , len (values ))
73
136
}
74
137
75
138
if values [0 ] == nil {
76
- return fmt .Sprintf ("%s > ? " , data .Field ), values [1 :], nil
139
+ return fmt .Sprintf ("%s > %s " , data .Field , ph ), values [1 :], nil
77
140
} else if values [1 ] == nil {
78
- return fmt .Sprintf ("%s < ? " , data .Field ), values [:1 ], nil
141
+ return fmt .Sprintf ("%s < %s " , data .Field , ph ), values [:1 ], nil
79
142
} else {
80
- return fmt .Sprintf ("( %s < ? OR %s > ? )" , data .Field , data .Field ), values , nil
143
+ return fmt .Sprintf ("( %s < %s OR %s > %s )" , data .Field , ph , data .Field , ph ), values , nil
81
144
}
82
145
case "between" :
83
146
if len (values ) != 2 {
84
147
return "" , nil , fmt .Errorf ("wrong number of parameters for notBetween operation: %d" , len (values ))
85
148
}
86
149
87
150
if values [0 ] == nil {
88
- return fmt .Sprintf ("%s < ? " , data .Field ), values [1 :], nil
151
+ return fmt .Sprintf ("%s < %s " , data .Field , ph ), values [1 :], nil
89
152
} else if values [1 ] == nil {
90
- return fmt .Sprintf ("%s > ? " , data .Field ), values [:1 ], nil
153
+ return fmt .Sprintf ("%s > %s " , data .Field , ph ), values [:1 ], nil
91
154
} else {
92
- return fmt .Sprintf ("( %s > ? AND %s < ? )" , data .Field , data .Field ), values , nil
155
+ return fmt .Sprintf ("( %s > %s AND %s < %s )" , data .Field , ph , data .Field , ph ), values , nil
93
156
}
94
157
case "greater" :
95
- return fmt .Sprintf ("%s > ? " , data .Field ), values , nil
158
+ return fmt .Sprintf ("%s > %s " , data .Field , ph ), values , nil
96
159
case "beginsWith" :
97
- search := "CONCAT(?, '%')"
160
+ var search string
161
+ switch DB {
162
+ case DB_MYSQL :
163
+ search = "CONCAT(?, '%')"
164
+ case DB_POSTGRESQL :
165
+ if isDynamicField {
166
+ search = "'\" ' || $ || '%'"
167
+ } else {
168
+ search = " $ || '%'"
169
+ }
170
+ }
98
171
return fmt .Sprintf ("%s LIKE %s" , data .Field , search ), values , nil
99
172
case "notBeginsWith" :
100
- search := "CONCAT(?, '%')"
173
+ var search string
174
+ switch DB {
175
+ case DB_MYSQL :
176
+ search = "CONCAT(?, '%')"
177
+ case DB_POSTGRESQL :
178
+ if isDynamicField {
179
+ search = "'\" ' || $ || '%'"
180
+ } else {
181
+ search = " $ || '%'"
182
+ }
183
+ }
101
184
return fmt .Sprintf ("%s NOT LIKE %s" , data .Field , search ), values , nil
102
185
case "endsWith" :
103
- search := "CONCAT('%', ?)"
186
+ var search string
187
+ switch DB {
188
+ case DB_MYSQL :
189
+ search = "CONCAT('%', ?)"
190
+ case DB_POSTGRESQL :
191
+ if isDynamicField {
192
+ search = "'%' || $ || '\" '"
193
+ } else {
194
+ search = "'%' || $ "
195
+ }
196
+ }
104
197
return fmt .Sprintf ("%s LIKE %s" , data .Field , search ), values , nil
105
198
case "notEndsWith" :
106
- search := "CONCAT('%', ?)"
199
+ var search string
200
+ switch DB {
201
+ case DB_MYSQL :
202
+ search = "CONCAT('%', ?)"
203
+ case DB_POSTGRESQL :
204
+ if isDynamicField {
205
+ search = "'%' || $ || '\" '"
206
+ } else {
207
+ search = "'%' || $ "
208
+ }
209
+ }
107
210
return fmt .Sprintf ("%s NOT LIKE %s" , data .Field , search ), values , nil
108
211
}
109
212
@@ -141,5 +244,43 @@ func GetSQL(data Filter, config *SQLConfig) (string, []interface{}, error) {
141
244
outStr = "( " + outStr + " )"
142
245
}
143
246
247
+ // number all placeholders
248
+ if DB == DB_POSTGRESQL {
249
+ n := 1
250
+ for strings .Contains (outStr , " $ " ) {
251
+ outStr = strings .Replace (outStr , " $ " , fmt .Sprintf ("$%d" , n ), 1 )
252
+ n = n + 1
253
+ }
254
+ }
255
+
144
256
return outStr , values , nil
145
257
}
258
+
259
+ func getPlaceholder () (string , error ) {
260
+ switch DB {
261
+ case DB_MYSQL :
262
+ return "?" , nil
263
+ case DB_POSTGRESQL :
264
+ return " $ " , nil
265
+ default :
266
+ return "" , fmt .Errorf ("unknown database" )
267
+ }
268
+ }
269
+
270
+ func getDynamicField (array []DynamicField , value string ) * DynamicField {
271
+ for _ , v := range array {
272
+ if v .Key == value {
273
+ return & v
274
+ }
275
+ }
276
+ return nil
277
+ }
278
+
279
+ func GetJSONBType (t string ) string {
280
+ switch t {
281
+ case "number" :
282
+ return "numeric"
283
+ default :
284
+ return t
285
+ }
286
+ }
0 commit comments