Mercurial > urweb
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, |