comparison src/sql.sml @ 2304:6fb9232ade99

Merge Sqlcache
author Adam Chlipala <adam@chlipala.net>
date Sun, 20 Dec 2015 14:18:52 -0500
parents e6c5bb62fef8
children
comparison
equal deleted inserted replaced
2201:1091227f535a 2304:6fb9232ade99
1 structure Sql = struct 1 structure Sql :> SQL = struct
2 2
3 open Mono 3 open Mono
4 4
5 val debug = ref false 5 val debug = ref false
6 6
18 | Lvar of lvar 18 | Lvar of lvar
19 | Func of func * exp list 19 | Func of func * exp list
20 | Recd of (string * exp) list 20 | Recd of (string * exp) list
21 | Proj of exp * string 21 | Proj of exp * string
22 22
23 datatype cmp =
24 Eq
25 | Ne
26 | Lt
27 | Le
28 | Gt
29 | Ge
30
23 datatype reln = 31 datatype reln =
24 Known 32 Known
25 | Sql of string 33 | Sql of string
26 | PCon0 of string 34 | PCon0 of string
27 | PCon1 of string 35 | PCon1 of string
28 | Eq 36 | Cmp of cmp
29 | Ne 37
30 | Lt 38 datatype lop =
31 | Le 39 And
32 | Gt 40 | Or
33 | Ge
34 41
35 datatype prop = 42 datatype prop =
36 True 43 True
37 | False 44 | False
38 | Unknown 45 | Unknown
39 | And of prop * prop 46 | Lop of lop * prop * prop
40 | Or of prop * prop
41 | Reln of reln * exp list 47 | Reln of reln * exp list
42 | Cond of exp * prop 48 | Cond of exp * prop
43 49
44 datatype chunk = 50 datatype chunk =
45 String of string 51 String of string
144 else 150 else
145 String (Substring.string after) :: chs') 151 String (Substring.string after) :: chs')
146 end 152 end
147 | _ => NONE 153 | _ => NONE
148 154
155 (* Used by primSqlcache. *)
156 fun optConst s chs =
157 case chs of
158 String s' :: chs => if String.isPrefix s s' then
159 SOME (s, if size s = size s' then
160 chs
161 else
162 String (String.extract (s', size s, NONE)) :: chs)
163 else
164 SOME ("", String s' :: chs)
165 | _ => NONE
166
149 fun ws p = wrap (follow (skip (fn ch => ch = #" ")) 167 fun ws p = wrap (follow (skip (fn ch => ch = #" "))
150 (follow p (skip (fn ch => ch = #" ")))) (#1 o #2) 168 (follow p (skip (fn ch => ch = #" ")))) (#1 o #2)
151 169
152 fun log name p chs = 170 fun log name p chs =
153 (if !debug then 171 (if !debug then
175 SOME (str (Char.toUpper (String.sub (s, 3))) 193 SOME (str (Char.toUpper (String.sub (s, 3)))
176 ^ String.extract (s, 4, NONE)) 194 ^ String.extract (s, 4, NONE))
177 else 195 else
178 NONE) 196 NONE)
179 197
180 val field = wrap (follow t_ident 198 val field = wrap (follow (opt (follow t_ident (const ".")))
181 (follow (const ".") 199 uw_ident)
182 uw_ident)) 200 (fn (SOME (t, ()), f) => (t, f)
183 (fn (t, ((), f)) => (t, f)) 201 | (NONE, f) => ("T", f)) (* Should probably deal with this MySQL/SQLite case better some day. *)
184 202
185 datatype Rel = 203 datatype Rel =
186 Exps of exp * exp -> prop 204 RCmp of cmp
187 | Props of prop * prop -> prop 205 | RLop of lop
188 206
189 datatype sqexp = 207 datatype sqexp =
190 SqConst of Prim.t 208 SqConst of Prim.t
191 | SqTrue 209 | SqTrue
192 | SqFalse 210 | SqFalse
198 | Inj of Mono.exp 216 | Inj of Mono.exp
199 | SqFunc of string * sqexp 217 | SqFunc of string * sqexp
200 | Unmodeled 218 | Unmodeled
201 | Null 219 | Null
202 220
203 fun cmp s r = wrap (const s) (fn () => Exps (fn (e1, e2) => Reln (r, [e1, e2]))) 221 fun cmp s r = wrap (const s) (fn () => RCmp r)
204 222
205 val sqbrel = altL [cmp "=" Eq, 223 val sqbrel = altL [cmp "=" Eq,
206 cmp "<>" Ne, 224 cmp "<>" Ne,
207 cmp "<=" Le, 225 cmp "<=" Le,
208 cmp "<" Lt, 226 cmp "<" Lt,
209 cmp ">=" Ge, 227 cmp ">=" Ge,
210 cmp ">" Gt, 228 cmp ">" Gt,
211 wrap (const "AND") (fn () => Props And), 229 wrap (const "AND") (fn () => RLop And),
212 wrap (const "OR") (fn () => Props Or)] 230 wrap (const "OR") (fn () => RLop Or)]
213 231
214 datatype ('a, 'b) sum = inl of 'a | inr of 'b 232 datatype ('a, 'b) sum = inl of 'a | inr of 'b
215 233
216 fun string chs = 234 fun string chs =
217 case chs of 235 case chs of
236 | SOME (s, []) => SOME (s, chs) 254 | SOME (s, []) => SOME (s, chs)
237 | SOME (s, cs) => SOME (s, String (String.implode cs) :: chs) 255 | SOME (s, cs) => SOME (s, String (String.implode cs) :: chs)
238 end 256 end
239 else 257 else
240 NONE 258 NONE
241 | _ => NONE 259 | _ => NONE
242 260
243 val prim = 261 val prim =
244 altL [wrap (follow (wrapP (follow (keep Char.isDigit) (follow (const ".") (keep Char.isDigit))) 262 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)))) 263 (fn (x, ((), y)) => Option.map Prim.Float (Real64.fromString (x ^ "." ^ y))))
246 (opt (const "::float8"))) #1, 264 (opt (const "::float8"))) #1,
247 wrap (follow (wrapP (keep Char.isDigit) 265 wrap (follow (wrapP (keep Char.isDigit)
248 (Option.map Prim.Int o Int64.fromString)) 266 (Option.map Prim.Int o Int64.fromString))
249 (opt (const "::int8"))) #1, 267 (opt (const "::int8"))) #1,
250 wrap (follow (opt (const "E")) (follow string (opt (const "::text")))) 268 wrap (follow (opt (const "E")) (follow string (opt (const "::text"))))
251 ((fn s => Prim.String (Prim.Normal, s)) o #1 o #2)] 269 ((fn s => Prim.String (Prim.Normal, s)) o #1 o #2)]
270
271 val primSqlcache =
272 (* Like [prim], but always uses [Prim.String]s. *)
273 let
274 fun wrapS p f = wrap p ((fn s => Prim.String (Prim.Normal, s)) o f)
275 in
276 altL [wrapS (follow (wrap (follow (keep Char.isDigit)
277 (follow (const ".") (keep Char.isDigit)))
278 (fn (x, ((), y)) => x ^ "." ^ y))
279 (optConst "::float8"))
280 op^,
281 wrapS (follow (keep Char.isDigit)
282 (optConst "::int8"))
283 op^,
284 wrapS (follow (optConst "E") (follow string (optConst "::text")))
285 (fn (c1, (s, c2)) => c1 ^ s ^ c2)]
286 end
252 287
253 fun known' chs = 288 fun known' chs =
254 case chs of 289 case chs of
255 Exp (EFfi ("Basis", "sql_known"), _) :: chs => SOME ((), chs) 290 Exp (EFfi ("Basis", "sql_known"), _) :: chs => SOME ((), chs)
256 | _ => NONE 291 | _ => NONE
265 | Exp (ECase (e, [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, NONE), _), 300 | Exp (ECase (e, [((PCon (_, PConFfi {mod = "Basis", con = "True", ...}, NONE), _),
266 (EPrim (Prim.String (Prim.Normal, "TRUE")), _)), 301 (EPrim (Prim.String (Prim.Normal, "TRUE")), _)),
267 ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, NONE), _), 302 ((PCon (_, PConFfi {mod = "Basis", con = "False", ...}, NONE), _),
268 (EPrim (Prim.String (Prim.Normal, "FALSE")), _))], _), _) :: chs => 303 (EPrim (Prim.String (Prim.Normal, "FALSE")), _))], _), _) :: chs =>
269 SOME (e, chs) 304 SOME (e, chs)
270 305
271 | _ => NONE 306 | _ => NONE
307
308 (* For sqlcache, we only care that we can do string equality on injected Mono
309 expressions, so accept any expression without modifying it. *)
310 val sqlifySqlcache =
311 fn Exp e :: chs => SOME (e, chs)
312 | _ => NONE
272 313
273 fun constK s = wrap (const s) (fn () => s) 314 fun constK s = wrap (const s) (fn () => s)
274 315
275 val funcName = altL [constK "COUNT", 316 val funcName = altL [constK "COUNT",
276 constK "MIN", 317 constK "MIN",
277 constK "MAX", 318 constK "MAX",
278 constK "SUM", 319 constK "SUM",
279 constK "AVG"] 320 constK "AVG"]
280 321
322 fun arithmetic pExp = follow (const "(")
323 (follow pExp
324 (follow (altL (map const [" + ", " - ", " * ", " / ", " >> ", " << "]))
325 (follow pExp (const ")"))))
326
281 val unmodeled = altL [const "COUNT(*)", 327 val unmodeled = altL [const "COUNT(*)",
282 const "CURRENT_TIMESTAMP"] 328 const "CURRENT_TIMESTAMP"]
283 329
330 val sqlcacheMode = ref false;
331
284 fun sqexp chs = 332 fun sqexp chs =
285 log "sqexp" 333 log "sqexp"
286 (altL [wrap prim SqConst, 334 (altL [wrap (if !sqlcacheMode then primSqlcache else prim) SqConst,
287 wrap (const "TRUE") (fn () => SqTrue), 335 wrap (const "TRUE") (fn () => SqTrue),
288 wrap (const "FALSE") (fn () => SqFalse), 336 wrap (const "FALSE") (fn () => SqFalse),
289 wrap (const "NULL") (fn () => Null), 337 wrap (const "NULL") (fn () => Null),
290 wrap field Field, 338 wrap field Field,
291 wrap uw_ident Computed, 339 wrap uw_ident Computed,
292 wrap known SqKnown, 340 wrap known SqKnown,
293 wrap func SqFunc, 341 wrap func SqFunc,
342 wrap (arithmetic sqexp) (fn _ => Unmodeled),
294 wrap unmodeled (fn () => Unmodeled), 343 wrap unmodeled (fn () => Unmodeled),
295 wrap sqlify Inj, 344 wrap (if !sqlcacheMode then sqlifySqlcache else sqlify) Inj,
296 wrap (follow (const "COALESCE(") (follow sqexp (follow (const ",") 345 wrap (follow (const "COALESCE(") (follow sqexp (follow (const ",")
297 (follow (keep (fn ch => ch <> #")")) (const ")"))))) 346 (follow (keep (fn ch => ch <> #")")) (const ")")))))
298 (fn ((), (e, _)) => e), 347 (fn ((), (e, _)) => e),
299 wrap (follow (const "(NOT ") (follow sqexp (const ")"))) 348 wrap (follow (const "(NOT ") (follow sqexp (const ")")))
300 (fn ((), (e, _)) => SqNot e), 349 (fn ((), (e, _)) => SqNot e),
315 (fn ((), (e, ())) => e)]) 364 (fn ((), (e, ())) => e)])
316 chs 365 chs
317 366
318 and known chs = wrap (follow known' (follow (const "(") (follow sqexp (const ")")))) 367 and known chs = wrap (follow known' (follow (const "(") (follow sqexp (const ")"))))
319 (fn ((), ((), (e, ()))) => e) chs 368 (fn ((), ((), (e, ()))) => e) chs
320 369
321 and func chs = wrap (follow funcName (follow (const "(") (follow sqexp (const ")")))) 370 and func chs = wrap (follow funcName (follow (const "(") (follow sqexp (const ")"))))
322 (fn (f, ((), (e, ()))) => (f, e)) chs 371 (fn (f, ((), (e, ()))) => (f, e)) chs
323 372
324 datatype sitem = 373 datatype sitem =
325 SqField of string * string 374 SqField of string * string
331 380
332 val select = log "select" 381 val select = log "select"
333 (wrap (follow (const "SELECT ") (list sitem)) 382 (wrap (follow (const "SELECT ") (list sitem))
334 (fn ((), ls) => ls)) 383 (fn ((), ls) => ls))
335 384
336 val fitem = wrap (follow uw_ident 385 datatype jtype = Inner | Left | Right | Full
337 (follow (const " AS ") 386
338 t_ident)) 387 datatype fitem =
339 (fn (t, ((), f)) => (t, f)) 388 Table of string * string (* table AS name *)
340 389 | Join of jtype * fitem * fitem * sqexp
341 val from = log "from" 390 | Nested of query * string (* query AS name *)
342 (wrap (follow (const "FROM ") (list fitem)) 391
343 (fn ((), ls) => ls)) 392 and query =
393 Query1 of {Select : sitem list, From : fitem list, Where : sqexp option}
394 | Union of query * query
344 395
345 val wher = wrap (follow (ws (const "WHERE ")) sqexp) 396 val wher = wrap (follow (ws (const "WHERE ")) sqexp)
346 (fn ((), ls) => ls) 397 (fn ((), ls) => ls)
347 398
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" 399 val orderby = log "orderby"
361 (wrap (follow (ws (const "ORDER BY ")) 400 (wrap (follow (ws (const "ORDER BY "))
362 (follow (list sqexp) 401 (list (follow sqexp
363 (opt (ws (const "DESC"))))) 402 (opt (ws (const "DESC"))))))
364 ignore) 403 ignore)
365 404
366 fun query chs = log "query" 405 val jtype = altL [wrap (const "JOIN") (fn () => Inner),
367 (wrap 406 wrap (const "LEFT JOIN") (fn () => Left),
368 (follow 407 wrap (const "RIGHT JOIN") (fn () => Right),
369 (alt (wrap (follow (const "((") 408 wrap (const "FULL JOIN") (fn () => Full)]
370 (follow query 409
371 (follow (const ") UNION (") 410 fun fitem chs = altL [wrap (follow uw_ident
372 (follow query (const "))"))))) 411 (follow (const " AS ")
373 (fn ((), (q1, ((), (q2, ())))) => Union (q1, q2))) 412 t_ident))
374 (wrap query1 Query1)) 413 (fn (t, ((), f)) => Table (t, f)),
375 (opt orderby)) 414 wrap (follow (const "(")
376 #1) 415 (follow fitem
377 chs 416 (follow (ws jtype)
417 (follow fitem
418 (follow (const " ON ")
419 (follow sqexp
420 (const ")")))))))
421 (fn ((), (fi1, (jt, (fi2, ((), (se, ())))))) =>
422 Join (jt, fi1, fi2, se)),
423 wrap (follow (const "(")
424 (follow query
425 (follow (const ") AS ") t_ident)))
426 (fn ((), (q, ((), f))) => Nested (q, f))]
427 chs
428
429 and query1 chs = log "query1"
430 (wrap (follow (follow select from) (opt wher))
431 (fn ((fs, ts), wher) => {Select = fs, From = ts, Where = wher}))
432 chs
433
434 and from chs = log "from"
435 (wrap (follow (const "FROM ") (list fitem))
436 (fn ((), ls) => ls))
437 chs
438
439 and query chs = log "query"
440 (wrap (follow
441 (alt (wrap (follow (const "((")
442 (follow query
443 (follow (const ") UNION (")
444 (follow query (const "))")))))
445 (fn ((), (q1, ((), (q2, ())))) => Union (q1, q2)))
446 (wrap query1 Query1))
447 (opt orderby))
448 #1)
449 chs
378 450
379 datatype dml = 451 datatype dml =
380 Insert of string * (string * sqexp) list 452 Insert of string * (string * sqexp) list
381 | Delete of string * sqexp 453 | Delete of string * sqexp
382 | Update of string * (string * sqexp) list * sqexp 454 | Update of string * (string * sqexp) list * sqexp
394 handle ListPair.UnequalLengths => NONE)) 466 handle ListPair.UnequalLengths => NONE))
395 467
396 val delete = log "delete" 468 val delete = log "delete"
397 (wrap (follow (const "DELETE FROM ") 469 (wrap (follow (const "DELETE FROM ")
398 (follow uw_ident 470 (follow uw_ident
399 (follow (const " AS T_T WHERE ") 471 (follow (opt (const " AS T_T"))
400 sqexp))) 472 (opt (follow (const " WHERE ") sqexp)))))
401 (fn ((), (tab, ((), es))) => (tab, es))) 473 (fn ((), (tab, (_, wher))) => (tab, case wher of
474 SOME (_, es) => es
475 | NONE => SqTrue)))
402 476
403 val setting = log "setting" 477 val setting = log "setting"
404 (wrap (follow uw_ident (follow (const " = ") sqexp)) 478 (wrap (follow uw_ident (follow (const " = ") sqexp))
405 (fn (f, ((), e)) => (f, e))) 479 (fn (f, ((), e)) => (f, e)))
406 480
407 val update = log "update" 481 val update = log "update"
408 (wrap (follow (const "UPDATE ") 482 (wrap (follow (const "UPDATE ")
409 (follow uw_ident 483 (follow uw_ident
410 (follow (const " AS T_T SET ") 484 (follow (follow (opt (const " AS T_T")) (const " SET "))
411 (follow (list setting) 485 (follow (list setting)
412 (follow (ws (const "WHERE ")) 486 (follow (ws (const "WHERE "))
413 sqexp))))) 487 sqexp)))))
414 (fn ((), (tab, ((), (fs, ((), e))))) => 488 (fn ((), (tab, (_, (fs, ((), e))))) =>
415 (tab, fs, e))) 489 (tab, fs, e)))
416 490
417 val dml = log "dml" 491 val dml = log "dml"
418 (altL [wrap insert Insert, 492 (altL [wrap insert Insert,
419 wrap delete Delete, 493 wrap delete Delete,