-
Notifications
You must be signed in to change notification settings - Fork 2
/
honeysql_select.clj
96 lines (75 loc) · 3.04 KB
/
honeysql_select.clj
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
(ns demo.honeysql-select
"Demo: Parse SQL SELECT query to HoneySQL data structures."
{:clj-kondo/config '{:linters {:missing-docstring {:level :off}}}}
(:require [strojure.parsesso.char :as char]
[strojure.parsesso.parser :as p]))
(set! *warn-on-reflection* true)
;;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
(comment
"SELECT u.username, s.name FROM user AS u, status AS s WHERE (u.statusid = s.id) AND (u.id = ?)"
{:select [:u.username :s.name]
:from [[:user :u] [:status :s]]
:where [:and [:= :u.statusid :s.id]
[:= :u.id 9]]}
"SELECT username, name FROM user, status WHERE (user.statusid = status.id) AND (user.id = ?)"
{:select [:username :name]
:from [:user :status]
:where [:and [:= :user.statusid :status.id]
[:= :user.id 9]]})
;;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
(def *space (p/*skip char/white?))
(def +space (p/+skip char/white?))
(defn comma-sep
"Parses `p` separated by commas."
[p]
(p/+sep-by p (p/maybe (-> (char/is \,)
(p/between *space)))))
(def table-name
"Parses table name as `:table`."
(-> (p/+many char/letter?)
(p/value char/str* keyword)))
(def column-name
"Parses column as `:column` or `:table.column`."
(-> (p/group (p/option (p/maybe (p/group (p/+many char/letter?)
(char/is \.))))
(p/+many char/letter?))
(p/value char/str* keyword)))
(comment
(p/parse column-name "username") #_=> :username
(p/parse column-name "u.username") #_=> :u.username
(p/parse column-name "u.u.username") #_=> :u.u
)
(def as-expr
"Parses alias keyword like `:alias` after AS."
(p/after (p/maybe (-> (p/word "as" :ic) (p/between +space)))
(-> (p/+many char/letter?)
(p/value char/str* keyword))))
(comment
(p/parse as-expr " AS name") #_=> :name
)
(defn with-as
"Parses `p` with optional alias like `:name` or `[:name :alias]`."
[p]
(-> (p/group p (p/option as-expr))
(p/value (fn [[x as]] (if as [x as] x)))))
(comment
(p/parse (with-as column-name) "u.username") #_=> :u.username
(p/parse (with-as column-name) "u.username AS name") #_=> [:u.username :name]
)
;;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
(def select-statement
"Parses SQL SELECT statement to `{:select [...] :from [...] ...}`."
(p/for [_ (p/maybe (p/after (p/word "select" :ic) +space))
select (comma-sep (with-as column-name))
_ (-> (p/word "from" :ic) (p/between +space))
from (comma-sep (with-as table-name))]
(p/result
{:select (vec select)
:from (vec from)})))
(comment
(def -q "SELECT username, u.name AS x FROM user AS u, status")
(p/parse select-statement -q)
#_=> {:select [:username [:u.name :x]],
:from [[:user :u] :status]}
)
;;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,