comparison src/sqlcache.sml @ 2289:78820fa8f5a7

Fix bugs for lock calculation and SQL parsing and add support for tasks.
author Ziv Scully <ziv@mit.edu>
date Sun, 15 Nov 2015 14:18:35 -0500
parents 98f96a976ede
children 50ad02829abd
comparison
equal deleted inserted replaced
2288:98f96a976ede 2289:78820fa8f5a7
1 structure Sqlcache :> SQLCACHE = struct 1 structure Sqlcache (* DEBUG :> SQLCACHE *) = struct
2 2
3 3
4 (*********************) 4 (*********************)
5 (* General Utilities *) 5 (* General Utilities *)
6 (*********************) 6 (*********************)
310 in 310 in
311 removeRedundant' (zs, []) 311 removeRedundant' (zs, [])
312 end 312 end
313 313
314 datatype atomExp = 314 datatype atomExp =
315 QueryArg of int 315 True
316 | False
317 | QueryArg of int
316 | DmlRel of int 318 | DmlRel of int
317 | Prim of Prim.t 319 | Prim of Prim.t
318 | Field of string * string 320 | Field of string * string
319 321
320 structure AtomExpKey : ORD_KEY = struct 322 structure AtomExpKey : ORD_KEY = struct
321 323
322 type ord_key = atomExp 324 type ord_key = atomExp
323 325
324 val compare = 326 val compare =
325 fn (QueryArg n1, QueryArg n2) => Int.compare (n1, n2) 327 fn (True, True) => EQUAL
328 | (True, _) => LESS
329 | (_, True) => GREATER
330 | (False, False) => EQUAL
331 | (False, _) => LESS
332 | (_, False) => GREATER
333 | (QueryArg n1, QueryArg n2) => Int.compare (n1, n2)
326 | (QueryArg _, _) => LESS 334 | (QueryArg _, _) => LESS
327 | (_, QueryArg _) => GREATER 335 | (_, QueryArg _) => GREATER
328 | (DmlRel n1, DmlRel n2) => Int.compare (n1, n2) 336 | (DmlRel n1, DmlRel n2) => Int.compare (n1, n2)
329 | (DmlRel _, _) => LESS 337 | (DmlRel _, _) => LESS
330 | (_, DmlRel _) => GREATER 338 | (_, DmlRel _) => GREATER
529 | (_, NONE, _, sq as SOME _) => wrap sq 537 | (_, NONE, _, sq as SOME _) => wrap sq
530 (* Last case should suffice because we don't 538 (* Last case should suffice because we don't
531 project from a sqlified value (which is a 539 project from a sqlified value (which is a
532 string). *) 540 string). *)
533 | (_, sq as SOME _, [], NONE) => wrap sq 541 | (_, sq as SOME _, [], NONE) => wrap sq
534 | _ => raise Match 542 | _ => raise Fail "Sqlcache: traverseSubst"
535 end) 543 end)
536 (f n) 544 (f n)
537 in 545 in
538 traverseIM ops (fn (_, v) => mp v) 546 traverseIM ops (fn (_, v) => mp v)
539 end 547 end
618 <\obind\> 626 <\obind\>
619 (fn arg => 627 (fn arg =>
620 AM.find (argsMap, arg) 628 AM.find (argsMap, arg)
621 <\obind\> 629 <\obind\>
622 (fn n' => SOME (ERel n'))) 630 (fn n' => SOME (ERel n')))
623 | _ => raise Match 631 | _ => raise Fail "Sqlcache: query (a)"
624 in 632 in
625 case (map #1 qs) of 633 case (map #1 qs) of
626 (q :: qs) => 634 (q :: qs) =>
627 let 635 let
628 val q = List.foldl Sql.Union q qs 636 val q = List.foldl Sql.Union q qs
629 val ns = IS.listItems (varsOfQuery q) 637 val ns = IS.listItems (varsOfQuery q)
630 val rename = 638 val rename =
631 fn ERel n => omap ERel (indexOf (fn n' => n' = n) ns) 639 fn ERel n => omap ERel (indexOf (fn n' => n' = n) ns)
632 | _ => raise Match 640 | _ => raise Fail "Sqlcache: query (b)"
633 in 641 in
634 case omapQuery rename q of 642 case omapQuery rename q of
635 SOME q => q 643 SOME q => q
636 (* We should never get NONE because indexOf should never fail. *) 644 (* We should never get NONE because indexOf should never fail. *)
637 | NONE => raise Match 645 | NONE => raise Fail "Sqlcache: query (c)"
638 end 646 end
639 (* We should never reach this case because [updateState] won't 647 (* We should never reach this case because [updateState] won't
640 put anything in the state if there are no queries. *) 648 put anything in the state if there are no queries. *)
641 | [] => raise Match 649 | [] => raise Fail "Sqlcache: query (d)"
642 end 650 end
643 651
644 val argOfExp = 652 val argOfExp =
645 let 653 let
646 fun doFields acc exp = 654 fun doFields acc exp =
698 | Sql.SqFalse => Combo (Disj, []) 706 | Sql.SqFalse => Combo (Disj, [])
699 | Sql.SqNot e => Negate (sqexpToFormula e) 707 | Sql.SqNot e => Negate (sqexpToFormula e)
700 | Sql.Binop (Sql.RCmp c, e1, e2) => Atom (c, e1, e2) 708 | Sql.Binop (Sql.RCmp c, e1, e2) => Atom (c, e1, e2)
701 | Sql.Binop (Sql.RLop l, p1, p2) => Combo (case l of Sql.And => Conj | Sql.Or => Disj, 709 | Sql.Binop (Sql.RLop l, p1, p2) => Combo (case l of Sql.And => Conj | Sql.Or => Disj,
702 [sqexpToFormula p1, sqexpToFormula p2]) 710 [sqexpToFormula p1, sqexpToFormula p2])
711 | e as Sql.Field f => Atom (Sql.Eq, e, Sql.SqTrue)
703 (* ASK: any other sqexps that can be props? *) 712 (* ASK: any other sqexps that can be props? *)
704 | _ => raise Match 713 | Sql.SqConst prim =>
714 (case prim of
715 (Prim.String (Prim.Normal, s)) =>
716 if s = #trueString (Settings.currentDbms ())
717 then Combo (Conj, [])
718 else if s = #falseString (Settings.currentDbms ())
719 then Combo (Disj, [])
720 else raise Fail "Sqlcache: sqexpToFormula (SqConst a)"
721 | _ => raise Fail "Sqlcache: sqexpToFormula (SqConst b)")
722 | Sql.Computed _ => raise Fail "Sqlcache: sqexpToFormula (Computed)"
723 | Sql.SqKnown _ => raise Fail "Sqlcache: sqexpToFormula (SqKnown)"
724 | Sql.Inj _ => raise Fail "Sqlcache: sqexpToFormula (Inj)"
725 | Sql.SqFunc _ => raise Fail "Sqlcache: sqexpToFormula (SqFunc)"
726 | Sql.Unmodeled => raise Fail "Sqlcache: sqexpToFormula (Unmodeled)"
727 | Sql.Null => raise Fail "Sqlcache: sqexpToFormula (Null)"
705 728
706 fun mapSqexpFields f = 729 fun mapSqexpFields f =
707 fn Sql.Field (t, v) => f (t, v) 730 fn Sql.Field (t, v) => f (t, v)
708 | Sql.SqNot e => Sql.SqNot (mapSqexpFields f e) 731 | Sql.SqNot e => Sql.SqNot (mapSqexpFields f e)
709 | Sql.Binop (r, e1, e2) => Sql.Binop (r, mapSqexpFields f e1, mapSqexpFields f e2) 732 | Sql.Binop (r, e1, e2) => Sql.Binop (r, mapSqexpFields f e1, mapSqexpFields f e2)
797 | _ => NONE 820 | _ => NONE
798 821
799 fun equivClasses atoms : atomExp list list option = 822 fun equivClasses atoms : atomExp list list option =
800 let 823 let
801 val uf = List.foldl UF.union' UF.empty (List.mapPartial toKnownEquality atoms) 824 val uf = List.foldl UF.union' UF.empty (List.mapPartial toKnownEquality atoms)
802 val ineqs = List.filter (fn (cmp, _, _) =>
803 cmp = Sql.Ne orelse cmp = Sql.Lt orelse cmp = Sql.Gt)
804 atoms
805 val contradiction = 825 val contradiction =
806 fn (cmp, SOME ae1, SOME ae2) => (cmp = Sql.Ne orelse cmp = Sql.Lt orelse cmp = Sql.Gt) 826 fn (cmp, SOME ae1, SOME ae2) => (cmp = Sql.Ne orelse cmp = Sql.Lt orelse cmp = Sql.Gt)
807 andalso UF.together (uf, ae1, ae2) 827 andalso UF.together (uf, ae1, ae2)
808 (* If we don't know one side of the comparision, not a contradiction. *) 828 (* If we don't know one side of the comparision, not a contradiction. *)
809 | _ => false 829 | _ => false
926 let 946 let
927 val loc = dummyLoc 947 val loc = dummyLoc
928 in 948 in
929 List.foldl (fn (e', seq) => ESeq ((seq, loc), (e', loc))) exp exps 949 List.foldl (fn (e', seq) => ESeq ((seq, loc), (e', loc))) exp exps
930 end 950 end
931 | _ => raise Match 951 | _ => raise Fail "Sqlcache: sequence"
932 952
933 (* Always increments negative indices as a hack we use later. *) 953 (* Always increments negative indices as a hack we use later. *)
934 fun incRels inc = 954 fun incRels inc =
935 MonoUtil.Exp.mapB 955 MonoUtil.Exp.mapB
936 {typ = fn t' => t', 956 {typ = fn t' => t',
981 exp = fn env => fn e' => fn s => Search.Continue (doExp env e' s), 1001 exp = fn env => fn e' => fn s => Search.Continue (doExp env e' s),
982 decl = fn _ => Search.return2, 1002 decl = fn _ => Search.return2,
983 bind = doBind} 1003 bind = doBind}
984 MonoEnv.empty file start of 1004 MonoEnv.empty file start of
985 Search.Continue x => x 1005 Search.Continue x => x
986 | Search.Return _ => raise Match 1006 | Search.Return _ => raise Fail "Sqlcache: fileAllMapfoldB"
987 1007
988 fun fileMap doExp file = #1 (fileAllMapfoldB (fn _ => fn e => fn _ => (doExp e, ())) file ()) 1008 fun fileMap doExp file = #1 (fileAllMapfoldB (fn _ => fn e => fn _ => (doExp e, ())) file ())
989 1009
990 (* TODO: make this a bit prettier.... *) 1010 (* TODO: make this a bit prettier.... *)
991 val simplifySql = 1011 val simplifySql =
1027 fun doExp exp' = 1047 fun doExp exp' =
1028 let 1048 let
1029 val text = case exp' of 1049 val text = case exp' of
1030 EQuery {query = text, ...} => text 1050 EQuery {query = text, ...} => text
1031 | EDml (text, _) => text 1051 | EDml (text, _) => text
1032 | _ => raise Match 1052 | _ => raise Fail "Sqlcache: simplifySql (a)"
1033 val (newText, wrapLets, numArgs) = factorOutNontrivial text 1053 val (newText, wrapLets, numArgs) = factorOutNontrivial text
1034 val newExp' = case exp' of 1054 val newExp' = case exp' of
1035 EQuery q => EQuery {query = newText, 1055 EQuery q => EQuery {query = newText,
1036 exps = #exps q, 1056 exps = #exps q,
1037 tables = #tables q, 1057 tables = #tables q,
1038 state = #state q, 1058 state = #state q,
1039 body = #body q, 1059 body = #body q,
1040 initial = #initial q} 1060 initial = #initial q}
1041 | EDml (_, failureMode) => EDml (newText, failureMode) 1061 | EDml (_, failureMode) => EDml (newText, failureMode)
1042 | _ => raise Match 1062 | _ => raise Fail "Sqlcache: simplifySql (b)"
1043 in 1063 in
1044 (* Increment once for each new variable just made. This is 1064 (* Increment once for each new variable just made. This is
1045 where we use the negative De Bruijn indices hack. *) 1065 where we use the negative De Bruijn indices hack. *)
1046 (* TODO: please don't use that hack. As anyone could have 1066 (* TODO: please don't use that hack. As anyone could have
1047 predicted, it was incomprehensible a year later.... *) 1067 predicted, it was incomprehensible a year later.... *)
1126 fn (Cachable (_, f), state) => f state 1146 fn (Cachable (_, f), state) => f state
1127 | (Impure e, state) => (e, state) 1147 | (Impure e, state) => (e, state)
1128 1148
1129 val invalInfoOfSubexp = 1149 val invalInfoOfSubexp =
1130 fn Cachable (invalInfo, _) => invalInfo 1150 fn Cachable (invalInfo, _) => invalInfo
1131 | Impure _ => raise Match 1151 | Impure _ => raise Fail "Sqlcache: invalInfoOfSubexp"
1132 1152
1133 fun cacheWrap (env, exp, typ, args, index) = 1153 fun cacheWrap (env, exp, typ, args, index) =
1134 let 1154 let
1135 val loc = dummyLoc 1155 val loc = dummyLoc
1136 val rel0 = (ERel 0, loc) 1156 val rel0 = (ERel 0, loc)
1273 case attempt of 1293 case attempt of
1274 SOME (subexp, state) => (subexp, state) 1294 SOME (subexp, state) => (subexp, state)
1275 | NONE => mapFst Impure (mkExp state) 1295 | NONE => mapFst Impure (mkExp state)
1276 end 1296 end
1277 fun wrapBind1 f arg = 1297 fun wrapBind1 f arg =
1278 wrapBindN (fn [arg] => f arg | _ => raise Match) [arg] 1298 wrapBindN (fn [arg] => f arg
1299 | _ => raise Fail "Sqlcache: cacheTree (a)") [arg]
1279 fun wrapBind2 f (arg1, arg2) = 1300 fun wrapBind2 f (arg1, arg2) =
1280 wrapBindN (fn [arg1, arg2] => f (arg1, arg2) | _ => raise Match) [arg1, arg2] 1301 wrapBindN (fn [arg1, arg2] => f (arg1, arg2)
1302 | _ => raise Fail "Sqlcache: cacheTree (b)") [arg1, arg2]
1281 fun wrapN f es = wrapBindN f (map (fn e => ((env, e), Unknowns 0)) es) 1303 fun wrapN f es = wrapBindN f (map (fn e => ((env, e), Unknowns 0)) es)
1282 fun wrap1 f e = wrapBind1 f ((env, e), Unknowns 0) 1304 fun wrap1 f e = wrapBind1 f ((env, e), Unknowns 0)
1283 fun wrap2 f (e1, e2) = wrapBind2 f (((env, e1), Unknowns 0), ((env, e2), Unknowns 0)) 1305 fun wrap2 f (e1, e2) = wrapBind2 f (((env, e1), Unknowns 0), ((env, e2), Unknowns 0))
1284 in 1306 in
1285 case exp' of 1307 case exp' of
1304 | ECase (e, cases, {disc, result}) => 1326 | ECase (e, cases, {disc, result}) =>
1305 wrapBindN (fn (e::es) => 1327 wrapBindN (fn (e::es) =>
1306 ECase (e, 1328 ECase (e,
1307 (ListPair.map (fn (e, (p, _)) => (p, e)) (es, cases)), 1329 (ListPair.map (fn (e, (p, _)) => (p, e)) (es, cases)),
1308 {disc = disc, result = result}) 1330 {disc = disc, result = result})
1309 | _ => raise Match) 1331 | _ => raise Fail "Sqlcache: cacheTree (c)")
1310 (((env, e), Unknowns 0) 1332 (((env, e), Unknowns 0)
1311 :: map (fn (p, e) => 1333 :: map (fn (p, e) =>
1312 ((MonoEnv.patBinds env p, e), Unknowns (MonoEnv.patBindsN p))) 1334 ((MonoEnv.patBinds env p, e), Unknowns (MonoEnv.patBindsN p)))
1313 cases) 1335 cases)
1314 | EStrcat (e1, e2) => wrap2 EStrcat (e1, e2) 1336 | EStrcat (e1, e2) => wrap2 EStrcat (e1, e2)
1360 | SOME e => (ESome (stringTyp, 1382 | SOME e => (ESome (stringTyp,
1361 (case e of 1383 (case e of
1362 DmlRel n => ERel n 1384 DmlRel n => ERel n
1363 | Prim p => EPrim p 1385 | Prim p => EPrim p
1364 (* TODO: make new type containing only these two. *) 1386 (* TODO: make new type containing only these two. *)
1365 | _ => raise Match, 1387 | _ => raise Fail "Sqlcache: optionAtomExpToExp",
1366 loc)), 1388 loc)),
1367 loc) 1389 loc)
1368 1390
1369 fun eqsToInvalidation numArgs eqs = 1391 fun eqsToInvalidation numArgs eqs =
1370 List.tabulate (numArgs, (fn n => IM.find (eqs, n))) 1392 List.tabulate (numArgs, (fn n => IM.find (eqs, n)))
1407 SOME (map (fn i => (case IM.find (indexToInvalInfo, i) of 1429 SOME (map (fn i => (case IM.find (indexToInvalInfo, i) of
1408 SOME invalInfo => 1430 SOME invalInfo =>
1409 (i, invalidations (invalInfo, dmlParsed)) 1431 (i, invalidations (invalInfo, dmlParsed))
1410 (* TODO: fail more gracefully. *) 1432 (* TODO: fail more gracefully. *)
1411 (* This probably means invalidating everything.... *) 1433 (* This probably means invalidating everything.... *)
1412 | NONE => raise Match)) 1434 | NONE => raise Fail "Sqlcache: addFlushing (a)"))
1413 (SIMM.findList (tableToIndices, tableOfDml dmlParsed))) 1435 (SIMM.findList (tableToIndices, tableOfDml dmlParsed)))
1414 | NONE => NONE 1436 | NONE => NONE
1415 in 1437 in
1416 case inval of 1438 case inval of
1417 (* TODO: fail more gracefully. *) 1439 (* TODO: fail more gracefully. *)
1418 NONE => raise Match 1440 NONE => raise Fail "Sqlcache: addFlushing (b)"
1419 | SOME invs => sequence (flushes invs @ [dmlExp]) 1441 | SOME invs => sequence (flushes invs @ [dmlExp])
1420 end 1442 end
1421 | e' => e' 1443 | e' => e'
1422 val file = fileMap doExp file 1444 val file = fileMap doExp file
1423 1445
1430 (***********) 1452 (***********)
1431 (* Locking *) 1453 (* Locking *)
1432 (***********) 1454 (***********)
1433 1455
1434 (* TODO: do this less evilly by not relying on specific FFI names, please? *) 1456 (* TODO: do this less evilly by not relying on specific FFI names, please? *)
1435 fun locksNeeded file = 1457 fun locksNeeded (lockMap : {store : IIMM.multimap, flush : IIMM.multimap}) =
1458 MonoUtil.Exp.fold
1459 {typ = #2,
1460 exp = fn (EFfiApp ("Sqlcache", x, _), state as {store, flush}) =>
1461 (case Int.fromString (String.extract (x, 5, NONE)) of
1462 NONE => state
1463 | SOME index =>
1464 if String.isPrefix "flush" x
1465 then {store = store, flush = IS.add (flush, index)}
1466 else if String.isPrefix "store" x
1467 then {store = IS.add (store, index), flush = flush}
1468 else state)
1469 | (ENamed n, {store, flush}) =>
1470 {store = IS.union (store, IIMM.findSet (#store lockMap, n)),
1471 flush = IS.union (flush, IIMM.findSet (#flush lockMap, n))}
1472 | (_, state) => state}
1473 {store = IS.empty, flush = IS.empty}
1474
1475 fun lockMapOfFile file =
1436 transitiveAnalysis 1476 transitiveAnalysis
1437 (fn ((_, name, _, e, _), state) => 1477 (fn ((_, name, _, e, _), state) =>
1438 MonoUtil.Exp.fold 1478 let
1439 {typ = #2, 1479 val locks = locksNeeded state e
1440 exp = fn (EFfiApp ("Sqlcache", x, _), state as {store, flush}) => 1480 in
1441 (case Int.fromString (String.extract (x, 5, NONE)) of 1481 {store = IIMM.insertSet (#store state, name, #store locks),
1442 NONE => state 1482 flush = IIMM.insertSet (#flush state, name, #flush locks)}
1443 | SOME index => 1483 end)
1444 if String.isPrefix "flush" x
1445 then {store = store, flush = IIMM.insert (flush, name, index)}
1446 else if String.isPrefix "store" x
1447 then {store = IIMM.insert (store, name, index), flush = flush}
1448 else state)
1449 | (_, state) => state}
1450 state
1451 e)
1452 {store = IIMM.empty, flush = IIMM.empty} 1484 {store = IIMM.empty, flush = IIMM.empty}
1453 file 1485 file
1454 1486
1455 fun exports (decls, _) = 1487 fun exports (decls, _) =
1456 List.foldl (fn ((DExport (_, _, n, _, _, _), _), ns) => IS.add (ns, n) 1488 List.foldl (fn ((DExport (_, _, n, _, _, _), _), ns) => IS.add (ns, n)
1457 | ((DTask _, _), _) => raise Fail "Sqlcache doesn't yet support tasks."
1458 | (_, ns) => ns) 1489 | (_, ns) => ns)
1459 IS.empty 1490 IS.empty
1460 decls 1491 decls
1461 1492
1462 fun wrapLocks (locks, (exp', loc)) = 1493 fun wrapLocks (locks, (exp', loc)) =
1464 EAbs (s, t1, t2, exp) => (EAbs (s, t1, t2, wrapLocks (locks, exp)), loc) 1495 EAbs (s, t1, t2, exp) => (EAbs (s, t1, t2, wrapLocks (locks, exp)), loc)
1465 | _ => (List.foldr (fn (l, e') => sequence [lock l, e']) exp' locks, loc) 1496 | _ => (List.foldr (fn (l, e') => sequence [lock l, e']) exp' locks, loc)
1466 1497
1467 fun addLocking file = 1498 fun addLocking file =
1468 let 1499 let
1469 val {store, flush} = locksNeeded file 1500 val lockMap = lockMapOfFile file
1470 fun locks n = 1501 fun lockList {store, flush} =
1471 let 1502 let
1472 val wlocks = IIMM.findSet (flush, n) 1503 val ls = map (fn i => (i, true)) (IS.listItems flush)
1473 val rlocks = IIMM.findSet (store, n) 1504 @ map (fn i => (i, false)) (IS.listItems (IS.difference (store, flush)))
1474 val ls = map (fn i => (i, true)) (IS.listItems wlocks)
1475 @ map (fn i => (i, false)) (IS.listItems (IS.difference (rlocks, wlocks)))
1476 in 1505 in
1477 ListMergeSort.sort (fn ((i, _), (j, _)) => i > j) ls 1506 ListMergeSort.sort (fn ((i, _), (j, _)) => i > j) ls
1478 end 1507 end
1508 fun locksOfName n =
1509 lockList {store = IIMM.findSet (#flush lockMap, n),
1510 flush =IIMM.findSet (#store lockMap, n)}
1511 val locksOfExp = lockList o locksNeeded lockMap
1479 val expts = exports file 1512 val expts = exports file
1480 fun doVal (v as (x, n, t, exp, s)) = 1513 fun doVal (v as (x, n, t, exp, s)) =
1481 if IS.member (expts, n) 1514 if IS.member (expts, n)
1482 then (x, n, t, wrapLocks ((locks n), exp), s) 1515 then (x, n, t, wrapLocks ((locksOfName n), exp), s)
1483 else v 1516 else v
1484 val doDecl = 1517 val doDecl =
1485 fn (DVal v, loc) => (DVal (doVal v), loc) 1518 fn (DVal v, loc) => (DVal (doVal v), loc)
1486 | (DValRec vs, loc) => (DValRec (map doVal vs), loc) 1519 | (DValRec vs, loc) => (DValRec (map doVal vs), loc)
1520 | (DTask (exp1, exp2), loc) => (DTask (exp1, wrapLocks (locksOfExp exp2, exp2)), loc)
1487 | decl => decl 1521 | decl => decl
1488 in 1522 in
1489 mapFst (map doDecl) file 1523 mapFst (map doDecl) file
1490 end 1524 end
1491 1525