Mercurial > urweb
comparison src/sqlcache.sml @ 2294:f8903af753ff
Support nested queries but disable UrFlow for now.
author | Ziv Scully <ziv@mit.edu> |
---|---|
date | Thu, 19 Nov 2015 01:59:00 -0500 |
parents | 50ad02829abd |
children | e6c5bb62fef8 |
comparison
equal
deleted
inserted
replaced
2293:8be54d7bd06e | 2294:f8903af753ff |
---|---|
28 | 28 |
29 fun mapFst f (x, y) = (f x, y) | 29 fun mapFst f (x, y) = (f x, y) |
30 | 30 |
31 (* Option monad. *) | 31 (* Option monad. *) |
32 fun obind (x, f) = Option.mapPartial f x | 32 fun obind (x, f) = Option.mapPartial f x |
33 fun oguard (b, x) = if b then x else NONE | 33 fun oguard (b, x) = if b then x () else NONE |
34 fun omap f = fn SOME x => SOME (f x) | _ => NONE | 34 fun omap f = fn SOME x => SOME (f x) | _ => NONE |
35 fun omap2 f = fn (SOME x, SOME y) => SOME (f (x,y)) | _ => NONE | 35 fun omap2 f = fn (SOME x, SOME y) => SOME (f (x,y)) | _ => NONE |
36 fun osequence ys = List.foldr (omap2 op::) (SOME []) ys | 36 fun osequence ys = List.foldr (omap2 op::) (SOME []) ys |
37 | |
38 fun concatMap f xs = List.concat (map f xs) | |
39 | |
40 val rec cartesianProduct : 'a list list -> 'a list list = | |
41 fn [] => [[]] | |
42 | (xs :: xss) => concatMap (fn ys => concatMap (fn x => [x :: ys]) xs) | |
43 (cartesianProduct xss) | |
37 | 44 |
38 fun indexOf test = | 45 fun indexOf test = |
39 let | 46 let |
40 fun f n = | 47 fun f n = |
41 fn [] => NONE | 48 fn [] => NONE |
102 | (env, MonoUtil.Exp.Datatype (x, n, cs)) => MonoEnv.pushDatatype env x n cs | 109 | (env, MonoUtil.Exp.Datatype (x, n, cs)) => MonoEnv.pushDatatype env x n cs |
103 | 110 |
104 val dummyLoc = ErrorMsg.dummySpan | 111 val dummyLoc = ErrorMsg.dummySpan |
105 | 112 |
106 (* DEBUG *) | 113 (* DEBUG *) |
107 fun printExp msg exp = Print.preface ("SQLCACHE: " ^ msg ^ ":", MonoPrint.p_exp MonoEnv.empty exp) | 114 fun printExp msg exp = |
108 fun printExp' msg exp' = printExp msg (exp', dummyLoc) | 115 (Print.preface ("SQLCACHE: " ^ msg ^ ":", MonoPrint.p_exp MonoEnv.empty exp); exp) |
109 fun printTyp msg typ = Print.preface ("SQLCACHE: " ^ msg ^ ":", MonoPrint.p_typ MonoEnv.empty typ) | 116 fun printExp' msg exp' = (printExp msg (exp', dummyLoc); exp') |
110 fun printTyp' msg typ' = printTyp msg (typ', dummyLoc) | 117 fun printTyp msg typ = |
118 (Print.preface ("SQLCACHE: " ^ msg ^ ":", MonoPrint.p_typ MonoEnv.empty typ); typ) | |
119 fun printTyp' msg typ' = (printTyp msg (typ', dummyLoc); typ') | |
111 fun obindDebug printer (x, f) = | 120 fun obindDebug printer (x, f) = |
112 case x of | 121 case x of |
113 NONE => NONE | 122 NONE => NONE |
114 | SOME x' => case f x' of | 123 | SOME x' => case f x' of |
115 NONE => (printer (); NONE) | 124 NONE => (printer (); NONE) |
202 Atom' of 'atom | 211 Atom' of 'atom |
203 | Combo' of junctionType * 'atom formula' list | 212 | Combo' of junctionType * 'atom formula' list |
204 | 213 |
205 val flipJt = fn Conj => Disj | Disj => Conj | 214 val flipJt = fn Conj => Disj | Disj => Conj |
206 | 215 |
207 fun concatMap f xs = List.concat (map f xs) | |
208 | |
209 val rec cartesianProduct : 'a list list -> 'a list list = | |
210 fn [] => [[]] | |
211 | (xs :: xss) => concatMap (fn ys => concatMap (fn x => [x :: ys]) xs) | |
212 (cartesianProduct xss) | |
213 | |
214 (* Pushes all negation to the atoms.*) | 216 (* Pushes all negation to the atoms.*) |
215 fun pushNegate (normalizeAtom : bool * 'atom -> 'atom) (negating : bool) = | 217 fun pushNegate (normalizeAtom : bool * 'atom -> 'atom) (negating : bool) = |
216 fn Atom x => Atom' (normalizeAtom (negating, x)) | 218 fn Atom x => Atom' (normalizeAtom (negating, x)) |
217 | Negate f => pushNegate normalizeAtom (not negating) f | 219 | Negate f => pushNegate normalizeAtom (not negating) f |
218 | Combo (j, fs) => Combo' (if negating then flipJt j else j, | 220 | Combo (j, fs) => Combo' (if negating then flipJt j else j, |
347 end | 349 end |
348 | 350 |
349 structure AtomOptionKey = OptionKeyFn(AtomExpKey) | 351 structure AtomOptionKey = OptionKeyFn(AtomExpKey) |
350 | 352 |
351 val rec tablesOfQuery = | 353 val rec tablesOfQuery = |
352 fn Sql.Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs) | 354 fn Sql.Query1 {From = fitems, ...} => List.foldl SS.union SS.empty (map tableOfFitem fitems) |
353 | Sql.Union (q1, q2) => SS.union (tablesOfQuery q1, tablesOfQuery q2) | 355 | Sql.Union (q1, q2) => SS.union (tablesOfQuery q1, tablesOfQuery q2) |
356 and tableOfFitem = | |
357 fn Sql.Table (t, _) => SS.singleton t | |
358 | Sql.Nested (q, _) => tablesOfQuery q | |
359 | Sql.Join (_, f1, f2, _) => SS.union (tableOfFitem f1, tableOfFitem f2) | |
354 | 360 |
355 val tableOfDml = | 361 val tableOfDml = |
356 fn Sql.Insert (tab, _) => tab | 362 fn Sql.Insert (tab, _) => tab |
357 | Sql.Delete (tab, _) => tab | 363 | Sql.Delete (tab, _) => tab |
358 | Sql.Update (tab, _, _) => tab | 364 | Sql.Update (tab, _, _) => tab |
487 (* Traversal Utilities *) | 493 (* Traversal Utilities *) |
488 (* TODO: get rid of unused ones. *) | 494 (* TODO: get rid of unused ones. *) |
489 | 495 |
490 (* Need lift', etc. because we don't have rank-2 polymorphism. This should | 496 (* Need lift', etc. because we don't have rank-2 polymorphism. This should |
491 probably use a functor (an ML one, not Haskell) but works for now. *) | 497 probably use a functor (an ML one, not Haskell) but works for now. *) |
492 fun traverseSqexp (pure, _, lift, _, lift'', lift2, _) f = | 498 fun traverseSqexp (pure, _, _, _, lift, lift', _, _, lift2, _, _, _, _, _) f = |
493 let | 499 let |
494 val rec tr = | 500 val rec tr = |
495 fn Sql.SqNot se => lift Sql.SqNot (tr se) | 501 fn Sql.SqNot se => lift Sql.SqNot (tr se) |
496 | Sql.Binop (r, se1, se2) => | 502 | Sql.Binop (r, se1, se2) => |
497 lift2 (fn (trse1, trse2) => Sql.Binop (r, trse1, trse2)) (tr se1, tr se2) | 503 lift2 (fn (trse1, trse2) => Sql.Binop (r, trse1, trse2)) (tr se1, tr se2) |
498 | Sql.SqKnown se => lift Sql.SqKnown (tr se) | 504 | Sql.SqKnown se => lift Sql.SqKnown (tr se) |
499 | Sql.Inj (e', loc) => lift'' (fn fe' => Sql.Inj (fe', loc)) (f e') | 505 | Sql.Inj (e', loc) => lift' (fn fe' => Sql.Inj (fe', loc)) (f e') |
500 | Sql.SqFunc (s, se) => lift (fn trse => Sql.SqFunc (s, trse)) (tr se) | 506 | Sql.SqFunc (s, se) => lift (fn trse => Sql.SqFunc (s, trse)) (tr se) |
501 | se => pure se | 507 | se => pure se |
502 in | 508 in |
503 tr | 509 tr |
504 end | 510 end |
505 | 511 |
506 fun traverseQuery (ops as (_, pure', _, lift', _, _, lift2')) f = | 512 fun traverseFitem (ops as (_, _, _, pure''', _, _, _, lift''', _, _, _, _, lift2'''', lift2''''')) f = |
507 let | 513 let |
508 val rec mp = | 514 val rec tr = |
515 fn Sql.Table t => pure''' (Sql.Table t) | |
516 | Sql.Join (jt, fi1, fi2, se) => | |
517 lift2'''' (fn ((trfi1, trfi2), trse) => Sql.Join (jt, trfi1, trfi2, trse)) | |
518 (lift2''''' id (tr fi1, tr fi2), traverseSqexp ops f se) | |
519 | Sql.Nested (q, s) => lift''' (fn trq => Sql.Nested (trq, s)) | |
520 (traverseQuery ops f q) | |
521 in | |
522 tr | |
523 end | |
524 | |
525 and traverseQuery (ops as (_, pure', pure'', _, _, _, lift'', _, _, lift2', lift2'', lift2''', _, _)) f = | |
526 let | |
527 val rec seqList = | |
528 fn [] => pure'' [] | |
529 | (x::xs) => lift2''' op:: (x, seqList xs) | |
530 val rec tr = | |
509 fn Sql.Query1 q => | 531 fn Sql.Query1 q => |
510 (case #Where q of | 532 (* TODO: make sure we don't need to traverse [#Select q]. *) |
511 NONE => pure' (Sql.Query1 q) | 533 lift2' (fn (trfrom, trwher) => Sql.Query1 {Select = #Select q, |
512 | SOME se => | 534 From = trfrom, |
513 lift' (fn mpse => Sql.Query1 {Select = #Select q, | 535 Where = trwher}) |
514 From = #From q, | 536 (seqList (map (traverseFitem ops f) (#From q)), |
515 Where = SOME mpse}) | 537 case #Where q of |
516 (traverseSqexp ops f se)) | 538 NONE => pure' NONE |
517 | Sql.Union (q1, q2) => lift2' Sql.Union (mp q1, mp q2) | 539 | SOME se => lift'' SOME (traverseSqexp ops f se)) |
540 | Sql.Union (q1, q2) => lift2'' Sql.Union (tr q1, tr q2) | |
518 in | 541 in |
519 mp | 542 tr |
520 end | 543 end |
521 | 544 |
522 (* Include unused tuple elements in argument for convenience of using same | 545 (* Include unused tuple elements in argument for convenience of using same |
523 argument as [traverseQuery]. *) | 546 argument as [traverseQuery]. *) |
524 fun traverseIM (pure, _, _, _, _, lift2, _) f = | 547 fun traverseIM (pure, _, _, _, _, _, _, _, _, lift2, _, _, _, _) f = |
525 IM.foldli (fn (k, v, acc) => lift2 (fn (acc, w) => IM.insert (acc, k, w)) (acc, f (k,v))) | 548 IM.foldli (fn (k, v, acc) => lift2 (fn (acc, w) => IM.insert (acc, k, w)) (acc, f (k,v))) |
526 (pure IM.empty) | 549 (pure IM.empty) |
527 | 550 |
528 fun traverseSubst (ops as (_, pure', lift, _, _, _, lift2')) f = | 551 fun traverseSubst (ops as (_, pure', _, _, lift, _, _, _, _, lift2', _, _, _, _)) f = |
529 let | 552 let |
530 fun mp ((n, fields), sqlify) = | 553 fun mp ((n, fields), sqlify) = |
531 lift (fn ((n', fields'), sqlify') => | 554 lift (fn ((n', fields'), sqlify') => |
532 let | 555 let |
533 fun wrap sq = ((n', fields' @ fields), sq) | 556 fun wrap sq = ((n', fields' @ fields), sq) |
544 (f n) | 567 (f n) |
545 in | 568 in |
546 traverseIM ops (fn (_, v) => mp v) | 569 traverseIM ops (fn (_, v) => mp v) |
547 end | 570 end |
548 | 571 |
549 fun monoidOps plus zero = (fn _ => zero, fn _ => zero, | 572 fun monoidOps plus zero = |
550 fn _ => fn x => x, fn _ => fn x => x, fn _ => fn x => x, | 573 (fn _ => zero, fn _ => zero, fn _ => zero, fn _ => zero, |
551 fn _ => plus, fn _ => plus) | 574 fn _ => fn x => x, fn _ => fn x => x, fn _ => fn x => x, fn _ => fn x => x, |
552 | 575 fn _ => plus, fn _ => plus, fn _ => plus, fn _ => plus, fn _ => plus, fn _ => plus) |
553 val optionOps = (SOME, SOME, omap, omap, omap, omap2, omap2) | 576 |
577 val optionOps = (SOME, SOME, SOME, SOME, | |
578 omap, omap, omap, omap, | |
579 omap2, omap2, omap2, omap2, omap2, omap2) | |
554 | 580 |
555 fun foldMapQuery plus zero = traverseQuery (monoidOps plus zero) | 581 fun foldMapQuery plus zero = traverseQuery (monoidOps plus zero) |
556 val omapQuery = traverseQuery optionOps | 582 val omapQuery = traverseQuery optionOps |
557 fun foldMapIM plus zero = traverseIM (monoidOps plus zero) | 583 fun foldMapIM plus zero = traverseIM (monoidOps plus zero) |
558 fun omapIM f = traverseIM optionOps f | 584 fun omapIM f = traverseIM optionOps f |
725 | Sql.SqFunc _ => raise Fail "Sqlcache: sqexpToFormula (SqFunc)" | 751 | Sql.SqFunc _ => raise Fail "Sqlcache: sqexpToFormula (SqFunc)" |
726 | Sql.Unmodeled => raise Fail "Sqlcache: sqexpToFormula (Unmodeled)" | 752 | Sql.Unmodeled => raise Fail "Sqlcache: sqexpToFormula (Unmodeled)" |
727 | Sql.Null => raise Fail "Sqlcache: sqexpToFormula (Null)" | 753 | Sql.Null => raise Fail "Sqlcache: sqexpToFormula (Null)" |
728 | 754 |
729 fun mapSqexpFields f = | 755 fun mapSqexpFields f = |
730 fn Sql.Field (t, v) => f (t, v) | 756 fn Sql.Field (t, v) => f (t, v) |
731 | Sql.SqNot e => Sql.SqNot (mapSqexpFields f e) | 757 | Sql.SqNot e => Sql.SqNot (mapSqexpFields f e) |
732 | Sql.Binop (r, e1, e2) => Sql.Binop (r, mapSqexpFields f e1, mapSqexpFields f e2) | 758 | Sql.Binop (r, e1, e2) => Sql.Binop (r, mapSqexpFields f e1, mapSqexpFields f e2) |
733 | Sql.SqKnown e => Sql.SqKnown (mapSqexpFields f e) | 759 | Sql.SqKnown e => Sql.SqKnown (mapSqexpFields f e) |
734 | Sql.SqFunc (s, e) => Sql.SqFunc (s, mapSqexpFields f e) | 760 | Sql.SqFunc (s, e) => Sql.SqFunc (s, mapSqexpFields f e) |
735 | e => e | 761 | e => e |
742 | SOME (realTable, _) => realTable | 768 | SOME (realTable, _) => realTable |
743 in | 769 in |
744 mapSqexpFields (fn (t, f) => Sql.Field (rename t, f)) | 770 mapSqexpFields (fn (t, f) => Sql.Field (rename t, f)) |
745 end | 771 end |
746 | 772 |
747 fun queryToFormula marker = | 773 structure FlattenQuery = struct |
748 fn Sql.Query1 {Select = sitems, From = tablePairs, Where = wher} => | 774 |
749 let | 775 datatype substitution = RenameTable of string | SubstituteExp of Sql.sqexp SM.map |
750 val fWhere = case wher of | 776 |
751 NONE => Combo (Conj, []) | 777 fun applySubst substTable = |
752 | SOME e => sqexpToFormula (renameTables tablePairs e) | 778 let |
779 fun substitute (table, field) = | |
780 case SM.find (substTable, table) of | |
781 NONE => Sql.Field (table, field) | |
782 | SOME (RenameTable realTable) => Sql.Field (realTable, field) | |
783 | SOME (SubstituteExp substField) => | |
784 case SM.find (substField, field) of | |
785 NONE => raise Fail "Sqlcache: applySubst" | |
786 | SOME se => se | |
787 in | |
788 mapSqexpFields substitute | |
789 end | |
790 | |
791 fun addToSubst (substTable, table, substField) = | |
792 SM.insert (substTable, | |
793 table, | |
794 case substField of | |
795 RenameTable _ => substField | |
796 | SubstituteExp subst => SubstituteExp (SM.map (applySubst substTable) subst)) | |
797 | |
798 fun newSubst (t, s) = addToSubst (SM.empty, t, s) | |
799 | |
800 datatype sitem' = Named of Sql.sqexp * string | Unnamed of Sql.sqexp | |
801 | |
802 type queryFlat = {Select : sitem' list, Where : Sql.sqexp} | |
803 | |
804 val sitemsToSubst = | |
805 List.foldl (fn (Named (se, s), acc) => SM.insert (acc, s, se) | |
806 | (Unnamed _, _) => raise Fail "Sqlcache: sitemsToSubst") | |
807 SM.empty | |
808 | |
809 fun unionSubst (s1, s2) = SM.unionWith (fn _ => raise Fail "Sqlcache: unionSubst") (s1, s2) | |
810 | |
811 fun sqlAnd (se1, se2) = Sql.Binop (Sql.RLop Sql.And, se1, se2) | |
812 | |
813 val rec flattenFitem : Sql.fitem -> (Sql.sqexp * substitution SM.map) list = | |
814 fn Sql.Table (real, alias) => [(Sql.SqTrue, newSubst (alias, RenameTable real))] | |
815 | Sql.Nested (q, s) => | |
816 let | |
817 val qfs = flattenQuery q | |
818 in | |
819 map (fn (qf, subst) => | |
820 (#Where qf, addToSubst (subst, s, SubstituteExp (sitemsToSubst (#Select qf))))) | |
821 qfs | |
822 end | |
823 | Sql.Join (jt, fi1, fi2, se) => | |
824 concatMap (fn ((wher1, subst1)) => | |
825 map (fn (wher2, subst2) => | |
826 (sqlAnd (wher1, wher2), | |
827 (* There should be no name conflicts... Ziv hopes? *) | |
828 unionSubst (subst1, subst2))) | |
829 (flattenFitem fi2)) | |
830 (flattenFitem fi1) | |
831 | |
832 and flattenQuery : Sql.query -> (queryFlat * substitution SM.map) list = | |
833 fn Sql.Query1 q => | |
834 let | |
835 val fifss = cartesianProduct (map flattenFitem (#From q)) | |
836 in | |
837 map (fn fifs => | |
838 let | |
839 val subst = List.foldl (fn ((_, subst), acc) => unionSubst (acc, subst)) | |
840 SM.empty | |
841 fifs | |
842 val wher = List.foldr (fn ((wher, _), acc) => sqlAnd (wher, acc)) | |
843 (case #Where q of | |
844 NONE => Sql.SqTrue | |
845 | SOME wher => wher) | |
846 fifs | |
847 in | |
848 (* ASK: do we actually need to pass the substitution through here? *) | |
849 (* We use the substitution later, but it's not clear we | |
850 need any of its currently present fields again. *) | |
851 ({Select = map (fn Sql.SqExp (se, s) => Named (applySubst subst se, s) | |
852 | Sql.SqField tf => | |
853 Unnamed (applySubst subst (Sql.Field tf))) | |
854 (#Select q), | |
855 Where = applySubst subst wher}, | |
856 subst) | |
857 end) | |
858 fifss | |
859 end | |
860 | Sql.Union (q1, q2) => (flattenQuery q1) @ (flattenQuery q2) | |
861 | |
862 end | |
863 | |
864 val flattenQuery = map #1 o FlattenQuery.flattenQuery | |
865 | |
866 fun queryFlatToFormula marker {Select = sitems, Where = wher} = | |
867 let | |
868 val fWhere = sqexpToFormula wher | |
753 in | 869 in |
754 case marker of | 870 case marker of |
755 NONE => fWhere | 871 NONE => fWhere |
756 | SOME markFields => | 872 | SOME markFields => |
757 let | 873 let |
758 val fWhereMarked = mapFormulaExps markFields fWhere | 874 val fWhereMarked = mapFormulaExps markFields fWhere |
759 val toSqexp = | 875 val toSqexp = |
760 fn Sql.SqField tf => Sql.Field tf | 876 fn FlattenQuery.Named (se, _) => se |
761 | Sql.SqExp (se, _) => se | 877 | FlattenQuery.Unnamed se => se |
762 fun ineq se = Atom (Sql.Ne, se, markFields se) | 878 fun ineq se = Atom (Sql.Ne, se, markFields se) |
763 val fIneqs = Combo (Disj, map (ineq o renameTables tablePairs o toSqexp) sitems) | 879 val fIneqs = Combo (Disj, map (ineq o toSqexp) sitems) |
764 in | 880 in |
765 (Combo (Conj, | 881 (Combo (Conj, |
766 [fWhere, | 882 [fWhere, |
767 Combo (Disj, | 883 Combo (Disj, |
768 [Negate fWhereMarked, | 884 [Negate fWhereMarked, |
769 Combo (Conj, [fWhereMarked, fIneqs])])])) | 885 Combo (Conj, [fWhereMarked, fIneqs])])])) |
770 end | 886 end |
771 end | 887 end |
772 | Sql.Union (q1, q2) => Combo (Disj, [queryToFormula marker q1, queryToFormula marker q2]) | 888 |
889 fun queryToFormula marker q = Combo (Disj, map (queryFlatToFormula marker) (flattenQuery q)) | |
773 | 890 |
774 fun valsToFormula (markLeft, markRight) (table, vals) = | 891 fun valsToFormula (markLeft, markRight) (table, vals) = |
775 Combo (Conj, | 892 Combo (Conj, |
776 map (fn (field, v) => Atom (Sql.Eq, markLeft (Sql.Field (table, field)), markRight v)) | 893 map (fn (field, v) => Atom (Sql.Eq, markLeft (Sql.Field (table, field)), markRight v)) |
777 vals) | 894 vals) |
826 fn (cmp, SOME ae1, SOME ae2) => (cmp = Sql.Ne orelse cmp = Sql.Lt orelse cmp = Sql.Gt) | 943 fn (cmp, SOME ae1, SOME ae2) => (cmp = Sql.Ne orelse cmp = Sql.Lt orelse cmp = Sql.Gt) |
827 andalso UF.together (uf, ae1, ae2) | 944 andalso UF.together (uf, ae1, ae2) |
828 (* If we don't know one side of the comparision, not a contradiction. *) | 945 (* If we don't know one side of the comparision, not a contradiction. *) |
829 | _ => false | 946 | _ => false |
830 in | 947 in |
831 not (List.exists contradiction atoms) <\oguard\> SOME (UF.classes uf) | 948 not (List.exists contradiction atoms) <\oguard\> (fn _ => SOME (UF.classes uf)) |
832 end | 949 end |
833 | 950 |
834 fun addToEqs (eqs, n, e) = | 951 fun addToEqs (eqs, n, e) = |
835 case IM.find (eqs, n) of | 952 case IM.find (eqs, n) of |
836 (* Comparing to a constant is probably better than comparing to a | 953 (* Comparing to a constant is probably better than comparing to a |
904 val markDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula -> | 1021 val markDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula -> |
905 (Sql.cmp * atomExp option * atomExp option) formula = | 1022 (Sql.cmp * atomExp option * atomExp option) formula = |
906 mapFormula (toAtomExps DmlRel) | 1023 mapFormula (toAtomExps DmlRel) |
907 | 1024 |
908 (* No eqs should have key conflicts because no variable is in two | 1025 (* No eqs should have key conflicts because no variable is in two |
909 equivalence classes, so the [#1] could be [#2]. *) | 1026 equivalence classes. *) |
910 val mergeEqs : (atomExp IntBinaryMap.map option list | 1027 val mergeEqs : (atomExp IntBinaryMap.map option list |
911 -> atomExp IntBinaryMap.map option) = | 1028 -> atomExp IntBinaryMap.map option) = |
912 List.foldr (omap2 (IM.unionWith #1)) (SOME IM.empty) | 1029 List.foldr (omap2 (IM.unionWith (fn _ => raise Fail "Sqlcache: ConflictMaps.mergeEqs"))) |
1030 (SOME IM.empty) | |
913 | 1031 |
914 val simplify = | 1032 val simplify = |
915 map TS.listItems | 1033 map TS.listItems |
916 o removeRedundant (fn (x, y) => TS.isSubset (y, x)) | 1034 o removeRedundant (fn (x, y) => TS.isSubset (y, x)) |
917 o map (fn xs => TS.addList (TS.empty, xs)) | 1035 o map (fn xs => TS.addList (TS.empty, xs)) |
1006 | Search.Return _ => raise Fail "Sqlcache: fileAllMapfoldB" | 1124 | Search.Return _ => raise Fail "Sqlcache: fileAllMapfoldB" |
1007 | 1125 |
1008 fun fileMap doExp file = #1 (fileAllMapfoldB (fn _ => fn e => fn _ => (doExp e, ())) file ()) | 1126 fun fileMap doExp file = #1 (fileAllMapfoldB (fn _ => fn e => fn _ => (doExp e, ())) file ()) |
1009 | 1127 |
1010 (* TODO: make this a bit prettier.... *) | 1128 (* TODO: make this a bit prettier.... *) |
1129 (* TODO: factour out identical subexpressions to the same variable.... *) | |
1011 val simplifySql = | 1130 val simplifySql = |
1012 let | 1131 let |
1013 fun factorOutNontrivial text = | 1132 fun factorOutNontrivial text = |
1014 let | 1133 let |
1015 val loc = dummyLoc | 1134 val loc = dummyLoc |
1016 fun strcat (e1, e2) = (EStrcat (e1, e2), loc) | 1135 val strcat = |
1136 fn (e1, (EPrim (Prim.String (Prim.Normal, "")), _)) => e1 | |
1137 | ((EPrim (Prim.String (Prim.Normal, "")), _), e2) => e2 | |
1138 | (e1, e2) => (EStrcat (e1, e2), loc) | |
1017 val chunks = Sql.chunkify text | 1139 val chunks = Sql.chunkify text |
1018 val (newText, newVariables) = | 1140 val (newText, newVariables) = |
1019 (* Important that this is foldr (to oppose foldl below). *) | 1141 (* Important that this is foldr (to oppose foldl below). *) |
1020 List.foldr | 1142 List.foldr |
1021 (fn (chunk, (qText, newVars)) => | 1143 (fn (chunk, (qText, newVars)) => |
1191 getAlwaysConsolidate () | 1313 getAlwaysConsolidate () |
1192 orelse not (List.exists isAsIs args andalso List.exists (not o isAsIs) args) | 1314 orelse not (List.exists isAsIs args andalso List.exists (not o isAsIs) args) |
1193 end | 1315 end |
1194 | 1316 |
1195 fun cacheExp (env, exp', invalInfo, state : state) = | 1317 fun cacheExp (env, exp', invalInfo, state : state) = |
1196 case worthCaching exp' <\oguard\> typOfExp' env exp' of | 1318 case worthCaching exp' <\oguard\> (fn _ => typOfExp' env exp') of |
1197 NONE => NONE | 1319 NONE => NONE |
1198 | SOME (TFun _, _) => NONE | 1320 | SOME (TFun _, _) => NONE |
1199 | SOME typ => | 1321 | SOME typ => |
1200 let | 1322 let |
1201 val args = InvalInfo.orderArgs (invalInfo, (exp', dummyLoc)) | 1323 val args = InvalInfo.orderArgs (invalInfo, (exp', dummyLoc)) |
1202 in | 1324 in |
1203 shouldConsolidate args | 1325 shouldConsolidate args |
1204 <\oguard\> | 1326 <\oguard\> |
1205 List.foldr (fn (arg, acc) => | 1327 (fn _ => |
1206 acc | 1328 List.foldr (fn (arg, acc) => |
1207 <\obind\> | 1329 acc |
1208 (fn args' => | 1330 <\obind\> |
1209 (case arg of | 1331 (fn args' => |
1210 AsIs exp => SOME exp | 1332 (case arg of |
1211 | Urlify exp => | 1333 AsIs exp => SOME exp |
1212 typOfExp env exp | 1334 | Urlify exp => |
1213 <\obind\> | 1335 typOfExp env exp |
1214 (fn typ => (MonoFooify.urlify env (exp, typ)))) | 1336 <\obind\> |
1215 <\obind\> | 1337 (fn typ => (MonoFooify.urlify env (exp, typ)))) |
1216 (fn arg' => SOME (arg' :: args')))) | 1338 <\obind\> |
1217 (SOME []) | 1339 (fn arg' => SOME (arg' :: args')))) |
1218 args | 1340 (SOME []) |
1219 <\obind\> | 1341 args |
1220 (fn args' => | |
1221 cacheWrap (env, (exp', dummyLoc), typ, args', #index state) | |
1222 <\obind\> | 1342 <\obind\> |
1223 (fn cachedExp => | 1343 (fn args' => |
1224 SOME (cachedExp, InvalInfo.updateState (invalInfo, length args', state)))) | 1344 cacheWrap (env, (exp', dummyLoc), typ, args', #index state) |
1345 <\obind\> | |
1346 (fn cachedExp => | |
1347 SOME (cachedExp, | |
1348 InvalInfo.updateState (invalInfo, length args', state))))) | |
1225 end | 1349 end |
1226 | 1350 |
1227 fun cacheQuery (effs, env, q) : subexp = | 1351 fun cacheQuery (effs, env, q) : subexp = |
1228 let | 1352 let |
1229 (* We use dummyTyp here. I think this is okay because databases don't | 1353 (* We use dummyTyp here. I think this is okay because databases don't |
1236 bound | 1360 bound |
1237 env) | 1361 env) |
1238 val {query = queryText, initial, body, ...} = q | 1362 val {query = queryText, initial, body, ...} = q |
1239 val attempt = | 1363 val attempt = |
1240 (* Ziv misses Haskell's do notation.... *) | 1364 (* Ziv misses Haskell's do notation.... *) |
1241 (safe 0 queryText andalso safe 0 initial andalso safe 2 body) | 1365 (safe 0 (printExp "attempt" queryText) andalso safe 0 initial andalso safe 2 body) |
1242 <\oguard\> | 1366 <\oguard\> |
1243 Sql.parse Sql.query queryText | 1367 (fn _ => |
1244 <\obind\> | 1368 Sql.parse Sql.query (printExp "safe" queryText) |
1245 (fn queryParsed => | 1369 <\obind\> |
1246 let | 1370 (fn queryParsed => |
1247 val invalInfo = InvalInfo.singleton queryParsed | 1371 let |
1248 fun mkExp state = | 1372 val _ = (printExp "parsed" queryText) |
1249 case cacheExp (env, EQuery q, invalInfo, state) of | 1373 val invalInfo = InvalInfo.singleton queryParsed |
1250 NONE => ((EQuery q, dummyLoc), state) | 1374 fun mkExp state = |
1251 | SOME (cachedExp, state) => ((cachedExp, dummyLoc), state) | 1375 case cacheExp (env, EQuery q, invalInfo, state) of |
1252 in | 1376 NONE => ((EQuery q, dummyLoc), state) |
1253 SOME (Cachable (invalInfo, mkExp)) | 1377 | SOME (cachedExp, state) => ((cachedExp, dummyLoc), state) |
1254 end) | 1378 in |
1379 SOME (Cachable (invalInfo, mkExp)) | |
1380 end)) | |
1255 in | 1381 in |
1256 case attempt of | 1382 case attempt of |
1257 NONE => Impure (EQuery q, dummyLoc) | 1383 NONE => Impure (EQuery q, dummyLoc) |
1258 | SOME subexp => subexp | 1384 | SOME subexp => subexp |
1259 end | 1385 end |
1277 (ListPair.map | 1403 (ListPair.map |
1278 (fn (subexp, (_, unbinds)) => | 1404 (fn (subexp, (_, unbinds)) => |
1279 InvalInfo.unbind (invalInfoOfSubexp subexp, unbinds)) | 1405 InvalInfo.unbind (invalInfoOfSubexp subexp, unbinds)) |
1280 (subexps, args))) | 1406 (subexps, args))) |
1281 <\obind\> | 1407 <\obind\> |
1282 (fn invalInfo => | 1408 (fn invalInfo => |
1283 SOME (Cachable (invalInfo, | 1409 SOME (Cachable (invalInfo, |
1284 fn state => | 1410 fn state => |
1285 case cacheExp (env, | 1411 case cacheExp (env, |
1286 f (map (#2 o #1) args), | 1412 f (map (#2 o #1) args), |
1287 invalInfo, | 1413 invalInfo, |
1288 state) of | 1414 state) of |
1289 NONE => mkExp state | 1415 NONE => mkExp state |
1290 | SOME (e', state) => ((e', loc), state)), | 1416 | SOME (e', state) => ((e', loc), state)), |
1291 state)) | 1417 state)) |
1292 in | 1418 in |
1293 case attempt of | 1419 case attempt of |
1294 SOME (subexp, state) => (subexp, state) | 1420 SOME (subexp, state) => (subexp, state) |
1295 | NONE => mapFst Impure (mkExp state) | 1421 | NONE => mapFst Impure (mkExp state) |
1296 end | 1422 end |
1382 | SOME e => (ESome (stringTyp, | 1508 | SOME e => (ESome (stringTyp, |
1383 (case e of | 1509 (case e of |
1384 DmlRel n => ERel n | 1510 DmlRel n => ERel n |
1385 | Prim p => EPrim p | 1511 | Prim p => EPrim p |
1386 (* TODO: make new type containing only these two. *) | 1512 (* TODO: make new type containing only these two. *) |
1387 | _ => raise Fail "Sqlcache: optionAtomExpToExp", | 1513 | _ => raise Fail "Sqlcache: Invalidations.optionAtomExpToExp", |
1388 loc)), | 1514 loc)), |
1389 loc) | 1515 loc) |
1390 | 1516 |
1391 fun eqsToInvalidation numArgs eqs = | 1517 fun eqsToInvalidation numArgs eqs = |
1392 List.tabulate (numArgs, (fn n => IM.find (eqs, n))) | 1518 List.tabulate (numArgs, (fn n => IM.find (eqs, n))) |
1504 @ map (fn i => (i, false)) (IS.listItems (IS.difference (store, flush))) | 1630 @ map (fn i => (i, false)) (IS.listItems (IS.difference (store, flush))) |
1505 in | 1631 in |
1506 ListMergeSort.sort (fn ((i, _), (j, _)) => i > j) ls | 1632 ListMergeSort.sort (fn ((i, _), (j, _)) => i > j) ls |
1507 end | 1633 end |
1508 fun locksOfName n = | 1634 fun locksOfName n = |
1509 lockList {store = IIMM.findSet (#flush lockMap, n), | 1635 lockList {flush = IIMM.findSet (#flush lockMap, n), |
1510 flush =IIMM.findSet (#store lockMap, n)} | 1636 store = IIMM.findSet (#store lockMap, n)} |
1511 val locksOfExp = lockList o locksNeeded lockMap | 1637 val locksOfExp = lockList o locksNeeded lockMap |
1512 val expts = exports file | 1638 val expts = exports file |
1513 fun doVal (v as (x, n, t, exp, s)) = | 1639 fun doVal (v as (x, n, t, exp, s)) = |
1514 if IS.member (expts, n) | 1640 if IS.member (expts, n) |
1515 then (x, n, t, wrapLocks ((locksOfName n), exp), s) | 1641 then (x, n, t, wrapLocks ((locksOfName n), exp), s) |