comparison pkg/controllers/importqueue.go @ 3194:eeff2cc4ff9d

controllers: re-factored the SQL filter to a tree like structure to be of more general use.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 08 May 2019 13:11:30 +0200
parents 54a3e40cfbed
children 4c254651d80b
comparison
equal deleted inserted replaced
3193:8329c6d3cf2a 3194:eeff2cc4ff9d
130 return nil 130 return nil
131 } 131 }
132 return &ta 132 return &ta
133 } 133 }
134 134
135 func buildFilters(req *http.Request) (l, b, a *filterBuilder, err error) { 135 type filledStmt struct {
136 136 stmt strings.Builder
137 l = new(filterBuilder) 137 args []interface{}
138 a = new(filterBuilder) 138 }
139 b = new(filterBuilder) 139
140 func buildFilters(req *http.Request) (*filledStmt, *filledStmt, *filledStmt, error) {
141
142 var l, a, b filterAnd
140 143
141 var noBefore, noAfter bool 144 var noBefore, noAfter bool
142 145
143 var counting bool 146 cond := func(format string, args ...interface{}) {
144 147 term := &filterTerm{format: format, args: args}
145 switch count := strings.ToLower(req.FormValue("count")); count { 148 l = append(l, term)
146 case "1", "t", "true": 149 a = append(l, term)
147 counting = true 150 b = append(b, term)
148 l.stmt.WriteString(selectImportsCountSQL)
149 default:
150 l.stmt.WriteString(selectImportsSQL)
151 }
152 a.stmt.WriteString(selectAfterSQL)
153 b.stmt.WriteString(selectBeforeSQL)
154
155 cond := func(format string, v ...interface{}) {
156 l.and(format, v...)
157 a.and(format, v...)
158 b.and(format, v...)
159 } 151 }
160 152
161 if query := req.FormValue("query"); query != "" { 153 if query := req.FormValue("query"); query != "" {
162 query = "%" + query + "%" 154 query = "%" + query + "%"
163 cond(` (kind ILIKE $%d OR username ILIKE $%d OR signer ILIKE $%d OR `+ 155 cond(` (kind ILIKE $%d OR username ILIKE $%d OR signer ILIKE $%d OR `+
179 ids := toInt8Array(idss) 171 ids := toInt8Array(idss)
180 cond(" id = ANY($%d) ", ids) 172 cond(" id = ANY($%d) ", ids)
181 } 173 }
182 174
183 if from := req.FormValue("from"); from != "" { 175 if from := req.FormValue("from"); from != "" {
184 var fromTime time.Time 176 fromTime, err := time.Parse(models.ImportTimeFormat, from)
185 if fromTime, err = time.Parse(models.ImportTimeFormat, from); err != nil { 177 if err != nil {
186 return 178 return nil, nil, nil, err
187 } 179 }
188 l.and(" enqueued >= $%d ", fromTime) 180 l = append(l, buildFilterTerm("enqueued >= $%d", fromTime))
189 b.and(" enqueued < $%d", fromTime) 181 b = append(b, buildFilterTerm("enqueued < $%d", fromTime))
190 } else { 182 } else {
191 noBefore = true 183 noBefore = true
192 } 184 }
193 185
194 if to := req.FormValue("to"); to != "" { 186 if to := req.FormValue("to"); to != "" {
195 var toTime time.Time 187 toTime, err := time.Parse(models.ImportTimeFormat, to)
196 if toTime, err = time.Parse(models.ImportTimeFormat, to); err != nil { 188 if err != nil {
197 return 189 return nil, nil, nil, err
198 } 190 }
199 l.and(" enqueued <= $%d ", toTime) 191 l = append(l, buildFilterTerm("enqueued <= $%d", toTime))
200 a.and(" enqueued > $%d", toTime) 192 a = append(a, buildFilterTerm("enqueued > $%d", toTime))
201 } else { 193 } else {
202 noAfter = true 194 noAfter = true
203 } 195 }
204 196
205 switch warn := strings.ToLower(req.FormValue("warnings")); warn { 197 switch warn := strings.ToLower(req.FormValue("warnings")); warn {
206 case "1", "t", "true": 198 case "1", "t", "true":
207 cond(" id IN (SELECT id FROM warned) ") 199 cond(" id IN (SELECT id FROM warned) ")
208 } 200 }
209 201
210 if !l.hasCond { 202 fl := &filledStmt{}
211 l.stmt.WriteString(" TRUE ") 203 fa := &filledStmt{}
212 } 204 fb := &filledStmt{}
213 if !b.hasCond { 205
214 b.stmt.WriteString(" TRUE ") 206 fa.stmt.WriteString(selectAfterSQL)
215 } 207 fb.stmt.WriteString(selectBeforeSQL)
216 if !a.hasCond { 208
217 a.stmt.WriteString(" TRUE ") 209 var counting bool
210
211 switch count := strings.ToLower(req.FormValue("count")); count {
212 case "1", "t", "true":
213 counting = true
214 fl.stmt.WriteString(selectImportsCountSQL)
215 default:
216 fl.stmt.WriteString(selectImportsSQL)
217 }
218
219 if len(l) == 0 {
220 fl.stmt.WriteString(" TRUE ")
221 } else {
222 l.serialize(&fl.stmt, &fl.args)
223 }
224
225 if len(b) == 0 {
226 fb.stmt.WriteString(" TRUE ")
227 } else {
228 b.serialize(&fb.stmt, &fb.args)
229 }
230
231 if len(a) == 0 {
232 fa.stmt.WriteString(" TRUE ")
233 } else {
234 a.serialize(&fa.stmt, &fa.args)
218 } 235 }
219 236
220 if !counting { 237 if !counting {
221 l.stmt.WriteString(" ORDER BY enqueued DESC ") 238 fl.stmt.WriteString(" ORDER BY enqueued DESC ")
222 a.stmt.WriteString(" ORDER BY enqueued LIMIT 1") 239 fa.stmt.WriteString(" ORDER BY enqueued LIMIT 1")
223 b.stmt.WriteString(" ORDER BY enqueued DESC LIMIT 1") 240 fb.stmt.WriteString(" ORDER BY enqueued DESC LIMIT 1")
224 } 241 }
225 242
226 if noBefore { 243 if noBefore {
227 b = nil 244 fb = nil
228 } 245 }
229 if noAfter { 246 if noAfter {
230 a = nil 247 fa = nil
231 } 248 }
232 return 249
233 } 250 return fl, fb, fa, nil
234 251 }
235 func neighbored(ctx context.Context, conn *sql.Conn, fb *filterBuilder) *models.ImportTime { 252
253 func neighbored(ctx context.Context, conn *sql.Conn, fb *filledStmt) *models.ImportTime {
236 254
237 var when time.Time 255 var when time.Time
238 err := conn.QueryRowContext(ctx, fb.stmt.String(), fb.args...).Scan(&when) 256 err := conn.QueryRowContext(ctx, fb.stmt.String(), fb.args...).Scan(&when)
239 switch { 257 switch {
240 case err == sql.ErrNoRows: 258 case err == sql.ErrNoRows:
250 _ interface{}, 268 _ interface{},
251 req *http.Request, 269 req *http.Request,
252 conn *sql.Conn, 270 conn *sql.Conn,
253 ) (jr JSONResult, err error) { 271 ) (jr JSONResult, err error) {
254 272
255 var list, before, after *filterBuilder 273 var list, before, after *filledStmt
256 274
257 if list, before, after, err = buildFilters(req); err != nil { 275 if list, before, after, err = buildFilters(req); err != nil {
258 return 276 return
259 } 277 }
260 278