-
Notifications
You must be signed in to change notification settings - Fork 4
/
aggregate.cljc
78 lines (72 loc) · 3.09 KB
/
aggregate.cljc
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
(ns com.yetanalytics.flint.validate.aggregate
(:require [com.yetanalytics.flint.validate.variable :as vv]
[com.yetanalytics.flint.util :as u]
[com.yetanalytics.flint.validate.util :as vu]))
;; In a query level which uses aggregates, only expressions consisting of
;; aggregates and constants may be projected, with one exception.
;; When GROUP BY is given with one or more simple expressions consisting of
;; just a variable, those variables may be projected from the level.
;; GOOD:
;; SELECT (2 AS ?two) WHERE { ?x ?y ?z }
;; SELECT (SUM(?x) AS ?sum) WHERE { ?x ?y ?z }
;; SELECT ?x WHERE { ?x ?y ?z } GROUP BY ?x
;; BAD:
;; SELECT (AVG(?x)) AS ?avg) ((?x + ?y) AS ?sum) WHERE { ?x ?y ?z }
;; SELECT ?y ?z WHERE { ?x ?y ?z } GROUP BY ?x
(defn- validate-agg-select-clause
"Return a coll of invalid vars in a SELECT clause with aggregates, or
`nil` if valid."
[group-by-vars sel-clause]
(let [[_ bad-vars]
(reduce (fn [[valid-vars bad-vars] [k x]]
(case k
:ax/var
(if-not (valid-vars x)
[valid-vars (conj bad-vars x)]
[valid-vars bad-vars])
:select/expr-as-var
(let [[_ [expr v-kv]] x
[_ v] v-kv]
(if-some [bad-expr-vars
(->> expr
(vv/invalid-agg-expr-vars valid-vars)
not-empty)]
[valid-vars (concat bad-vars bad-expr-vars)]
;; Somehow already-projected vars are now valid,
;; at least according to Apache Jena's query parser
[(conj valid-vars v) bad-vars]))))
[group-by-vars []]
sel-clause)]
(not-empty bad-vars)))
(defn- validate-agg-select
[[[_select-k select] loc]]
(let [[_ select-cls] (u/get-kv-pair select :select)
[_ ?group-by] (u/get-kv-pair select :group-by)
group-by-vs (if ?group-by
(->> ?group-by
(map vv/group-by-projected-vars)
(filter some?)
set)
#{})
[sel-k sel-v] select-cls]
(case sel-k
:ax/wildcard
{:kind ::wildcard-group-by
:path (vu/zip-path loc)}
:select/var-or-exprs
(when-some [bad-vars (validate-agg-select-clause group-by-vs sel-v)]
{:kind ::invalid-aggregate-var
:variables bad-vars
:path (vu/zip-path loc)})
;; else - perhaps it is a CONSTRUCT, DESCRIBE, or ASK query instead
nil)))
(defn validate-agg-selects
"Validate, given `node-m` that contains a map from `SELECT` AST nodes to
their zipper locs, that any SELECT that includes aggregates are valid
according to the SPARQL spec. Returns `nil` if valid, a coll of error
maps otherwise."
[node-m]
(->> (:agg/select node-m)
(map validate-agg-select)
(filter some?)
not-empty))