comparison src/sql.sml @ 1697:cb0f05bdc183

Refactor SQL parsing code from Iflow to Sql, add querydml parser.
author Edward Z. Yang <ezyang@mit.edu>
date Mon, 12 Mar 2012 12:00:23 -0700
parents
children 4d64af730e35 606af2c9b828
comparison
equal deleted inserted replaced
1696:177547450bb9 1697:cb0f05bdc183
1 structure Sql = struct
2
3 open Mono
4
5 val debug = ref false
6
7 type lvar = int
8
9 datatype func =
10 DtCon0 of string
11 | DtCon1 of string
12 | UnCon of string
13 | Other of string
14
15 datatype exp =
16 Const of Prim.t
17 | Var of int
18 | Lvar of lvar
19 | Func of func * exp list
20 | Recd of (string * exp) list
21 | Proj of exp * string
22
23 datatype reln =
24 Known
25 | Sql of string
26 | PCon0 of string
27 | PCon1 of string
28 | Eq
29 | Ne
30 | Lt
31 | Le
32 | Gt
33 | Ge
34
35 datatype prop =
36 True
37 | False
38 | Unknown
39 | And of prop * prop
40 | Or of prop * prop
41 | Reln of reln * exp list
42 | Cond of exp * prop
43
44 datatype chunk =
45 String of string
46 | Exp of Mono.exp
47
48 fun chunkify e =
49 case #1 e of
50 EPrim (Prim.String s) => [String s]
51 | EStrcat (e1, e2) =>
52 let
53 val chs1 = chunkify e1
54 val chs2 = chunkify e2
55 in
56 case chs2 of
57 String s2 :: chs2' =>
58 (case List.last chs1 of
59 String s1 => List.take (chs1, length chs1 - 1) @ String (s1 ^ s2) :: chs2'
60 | _ => chs1 @ chs2)
61 | _ => chs1 @ chs2
62 end
63 | _ => [Exp e]
64
65 type 'a parser = chunk list -> ('a * chunk list) option
66
67 fun always v chs = SOME (v, chs)
68
69 fun parse p s =
70 case p (chunkify s) of
71 SOME (v, []) => SOME v
72 | _ => NONE
73
74 fun const s chs =
75 case chs of
76 String s' :: chs => if String.isPrefix s s' then
77 SOME ((), if size s = size s' then
78 chs
79 else
80 String (String.extract (s', size s, NONE)) :: chs)
81 else
82 NONE
83 | _ => NONE
84
85 fun follow p1 p2 chs =
86 case p1 chs of
87 NONE => NONE
88 | SOME (v1, chs) =>
89 case p2 chs of
90 NONE => NONE
91 | SOME (v2, chs) => SOME ((v1, v2), chs)
92
93 fun wrap p f chs =
94 case p chs of
95 NONE => NONE
96 | SOME (v, chs) => SOME (f v, chs)
97
98 fun wrapP p f chs =
99 case p chs of
100 NONE => NONE
101 | SOME (v, chs) =>
102 case f v of
103 NONE => NONE
104 | SOME r => SOME (r, chs)
105
106 fun alt p1 p2 chs =
107 case p1 chs of
108 NONE => p2 chs
109 | v => v
110
111 fun altL ps =
112 case rev ps of
113 [] => (fn _ => NONE)
114 | p :: ps =>
115 foldl (fn (p1, p2) => alt p1 p2) p ps
116
117 fun opt p chs =
118 case p chs of
119 NONE => SOME (NONE, chs)
120 | SOME (v, chs) => SOME (SOME v, chs)
121
122 fun skip cp chs =
123 case chs of
124 String "" :: chs => skip cp chs
125 | String s :: chs' => if cp (String.sub (s, 0)) then
126 skip cp (String (String.extract (s, 1, NONE)) :: chs')
127 else
128 SOME ((), chs)
129 | _ => SOME ((), chs)
130
131 fun keep cp chs =
132 case chs of
133 String "" :: chs => keep cp chs
134 | String s :: chs' =>
135 let
136 val (befor, after) = Substring.splitl cp (Substring.full s)
137 in
138 if Substring.isEmpty befor then
139 NONE
140 else
141 SOME (Substring.string befor,
142 if Substring.isEmpty after then
143 chs'
144 else
145 String (Substring.string after) :: chs')
146 end
147 | _ => NONE
148
149 fun ws p = wrap (follow (skip (fn ch => ch = #" "))
150 (follow p (skip (fn ch => ch = #" ")))) (#1 o #2)
151
152 fun log name p chs =
153 (if !debug then
154 (print (name ^ ": ");
155 app (fn String s => print s
156 | _ => print "???") chs;
157 print "\n")
158 else
159 ();
160 p chs)
161
162 fun list p chs =
163 altL [wrap (follow p (follow (ws (const ",")) (list p)))
164 (fn (v, ((), ls)) => v :: ls),
165 wrap (ws p) (fn v => [v]),
166 always []] chs
167
168 val ident = keep (fn ch => Char.isAlphaNum ch orelse ch = #"_")
169
170 val t_ident = wrapP ident (fn s => if String.isPrefix "T_" s then
171 SOME (String.extract (s, 2, NONE))
172 else
173 NONE)
174 val uw_ident = wrapP ident (fn s => if String.isPrefix "uw_" s andalso size s >= 4 then
175 SOME (str (Char.toUpper (String.sub (s, 3)))
176 ^ String.extract (s, 4, NONE))
177 else
178 NONE)
179
180 val field = wrap (follow t_ident
181 (follow (const ".")
182 uw_ident))
183 (fn (t, ((), f)) => (t, f))
184
185 datatype Rel =
186 Exps of exp * exp -> prop
187 | Props of prop * prop -> prop
188
189 datatype sqexp =
190 SqConst of Prim.t
191 | SqTrue
192 | SqFalse
193 | SqNot of sqexp
194 | Field of string * string
195 | Computed of string
196 | Binop of Rel * sqexp * sqexp
197 | SqKnown of sqexp
198 | Inj of Mono.exp
199 | SqFunc of string * sqexp
200 | Unmodeled
201 | Null
202
203 fun cmp s r = wrap (const s) (fn () => Exps (fn (e1, e2) => Reln (r, [e1, e2])))
204
205 val sqbrel = altL [cmp "=" Eq,
206 cmp "<>" Ne,
207 cmp "<=" Le,
208 cmp "<" Lt,
209 cmp ">=" Ge,
210 cmp ">" Gt,
211 wrap (const "AND") (fn () => Props And),
212 wrap (const "OR") (fn () => Props Or)]
213
214 datatype ('a, 'b) sum = inl of 'a | inr of 'b
215
216 fun string chs =
217 case chs of
218 String s :: chs =>
219 if size s >= 2 andalso String.sub (s, 0) = #"'" then
220 let
221 fun loop (cs, acc) =
222 case cs of
223 [] => NONE
224 | c :: cs =>
225 if c = #"'" then
226 SOME (String.implode (rev acc), cs)
227 else if c = #"\\" then
228 case cs of
229 c :: cs => loop (cs, c :: acc)
230 | _ => raise Fail "Iflow.string: Unmatched backslash escape"
231 else
232 loop (cs, c :: acc)
233 in
234 case loop (String.explode (String.extract (s, 1, NONE)), []) of
235 NONE => NONE
236 | SOME (s, []) => SOME (s, chs)
237 | SOME (s, cs) => SOME (s, String (String.implode cs) :: chs)
238 end
239 else
240 NONE
241 | _ => NONE
242
243 val prim =
244 altL [wrap (follow (wrapP (follow (keep Char.isDigit) (follow (const ".") (keep Char.isDigit)))
245 (fn (x, ((), y)) => Option.map Prim.Float (Real64.fromString (x ^ "." ^ y))))
246 (opt (const "::float8"))) #1,
247 wrap (follow (wrapP (keep Char.isDigit)
248 (Option.map Prim.Int o Int64.fromString))
249 (opt (const "::int8"))) #1,
250 wrap (follow (opt (const "E")) (follow string (opt (const "::text"))))
251 (Prim.String o #1 o #2)]
252
253 fun known' chs =
254 case chs of
255 Exp (EFfi ("Basis", "sql_known"), _) :: chs => SOME ((), chs)
256 | _ => NONE
257
258 fun sqlify chs =
259 case chs of
260 Exp (EFfiApp ("Basis", f, [(e, _)]), _) :: chs =>
261 if String.isPrefix "sqlify" f then
262 SOME (e, chs)
263 else
264 NONE
265 | Exp (ECase (e, [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, NONE), _),
266 (EPrim (Prim.String "TRUE"), _)),
267 ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, NONE), _),
268 (EPrim (Prim.String "FALSE"), _))], _), _) :: chs =>
269 SOME (e, chs)
270
271 | _ => NONE
272
273 fun constK s = wrap (const s) (fn () => s)
274
275 val funcName = altL [constK "COUNT",
276 constK "MIN",
277 constK "MAX",
278 constK "SUM",
279 constK "AVG"]
280
281 val unmodeled = altL [const "COUNT(*)",
282 const "CURRENT_TIMESTAMP"]
283
284 fun sqexp chs =
285 log "sqexp"
286 (altL [wrap prim SqConst,
287 wrap (const "TRUE") (fn () => SqTrue),
288 wrap (const "FALSE") (fn () => SqFalse),
289 wrap (const "NULL") (fn () => Null),
290 wrap field Field,
291 wrap uw_ident Computed,
292 wrap known SqKnown,
293 wrap func SqFunc,
294 wrap unmodeled (fn () => Unmodeled),
295 wrap sqlify Inj,
296 wrap (follow (const "COALESCE(") (follow sqexp (follow (const ",")
297 (follow (keep (fn ch => ch <> #")")) (const ")")))))
298 (fn ((), (e, _)) => e),
299 wrap (follow (const "(NOT ") (follow sqexp (const ")")))
300 (fn ((), (e, _)) => SqNot e),
301 wrap (follow (ws (const "("))
302 (follow (wrap
303 (follow sqexp
304 (alt
305 (wrap
306 (follow (ws sqbrel)
307 (ws sqexp))
308 inl)
309 (always (inr ()))))
310 (fn (e1, sm) =>
311 case sm of
312 inl (bo, e2) => Binop (bo, e1, e2)
313 | inr () => e1))
314 (const ")")))
315 (fn ((), (e, ())) => e)])
316 chs
317
318 and known chs = wrap (follow known' (follow (const "(") (follow sqexp (const ")"))))
319 (fn ((), ((), (e, ()))) => e) chs
320
321 and func chs = wrap (follow funcName (follow (const "(") (follow sqexp (const ")"))))
322 (fn (f, ((), (e, ()))) => (f, e)) chs
323
324 datatype sitem =
325 SqField of string * string
326 | SqExp of sqexp * string
327
328 val sitem = alt (wrap (follow sqexp (follow (const " AS ") uw_ident))
329 (fn (e, ((), s)) => SqExp (e, s)))
330 (wrap field SqField)
331
332 val select = log "select"
333 (wrap (follow (const "SELECT ") (list sitem))
334 (fn ((), ls) => ls))
335
336 val fitem = wrap (follow uw_ident
337 (follow (const " AS ")
338 t_ident))
339 (fn (t, ((), f)) => (t, f))
340
341 val from = log "from"
342 (wrap (follow (const "FROM ") (list fitem))
343 (fn ((), ls) => ls))
344
345 val wher = wrap (follow (ws (const "WHERE ")) sqexp)
346 (fn ((), ls) => ls)
347
348 type query1 = {Select : sitem list,
349 From : (string * string) list,
350 Where : sqexp option}
351
352 val query1 = log "query1"
353 (wrap (follow (follow select from) (opt wher))
354 (fn ((fs, ts), wher) => {Select = fs, From = ts, Where = wher}))
355
356 datatype query =
357 Query1 of query1
358 | Union of query * query
359
360 val orderby = log "orderby"
361 (wrap (follow (ws (const "ORDER BY "))
362 (follow (list sqexp)
363 (opt (ws (const "DESC")))))
364 ignore)
365
366 fun query chs = log "query"
367 (wrap
368 (follow
369 (alt (wrap (follow (const "((")
370 (follow query
371 (follow (const ") UNION (")
372 (follow query (const "))")))))
373 (fn ((), (q1, ((), (q2, ())))) => Union (q1, q2)))
374 (wrap query1 Query1))
375 (opt orderby))
376 #1)
377 chs
378
379 datatype dml =
380 Insert of string * (string * sqexp) list
381 | Delete of string * sqexp
382 | Update of string * (string * sqexp) list * sqexp
383
384 val insert = log "insert"
385 (wrapP (follow (const "INSERT INTO ")
386 (follow uw_ident
387 (follow (const " (")
388 (follow (list uw_ident)
389 (follow (const ") VALUES (")
390 (follow (list sqexp)
391 (const ")")))))))
392 (fn ((), (tab, ((), (fs, ((), (es, ())))))) =>
393 (SOME (tab, ListPair.zipEq (fs, es)))
394 handle ListPair.UnequalLengths => NONE))
395
396 val delete = log "delete"
397 (wrap (follow (const "DELETE FROM ")
398 (follow uw_ident
399 (follow (const " AS T_T WHERE ")
400 sqexp)))
401 (fn ((), (tab, ((), es))) => (tab, es)))
402
403 val setting = log "setting"
404 (wrap (follow uw_ident (follow (const " = ") sqexp))
405 (fn (f, ((), e)) => (f, e)))
406
407 val update = log "update"
408 (wrap (follow (const "UPDATE ")
409 (follow uw_ident
410 (follow (const " AS T_T SET ")
411 (follow (list setting)
412 (follow (ws (const "WHERE "))
413 sqexp)))))
414 (fn ((), (tab, ((), (fs, ((), e))))) =>
415 (tab, fs, e)))
416
417 val dml = log "dml"
418 (altL [wrap insert Insert,
419 wrap delete Delete,
420 wrap update Update])
421
422 datatype querydml =
423 Query of query
424 | Dml of dml
425
426 val querydml = log "querydml" (altL [wrap dml Dml, wrap query Query])
427
428 end