comparison src/sqlcache.sml @ 2209:0ca11d57c175

Cleans up interface (it's now a command line option) and renames project to "sqlcache" in the all-one-word style. Still has issues to do with concurrency, retrying transactions, and foreign function calls that either rely on state or have side effects.
author Ziv Scully <ziv@mit.edu>
date Sat, 31 May 2014 03:08:16 -0400
parents
children 388ba4dc7c96
comparison
equal deleted inserted replaced
2208:cb74460f046a 2209:0ca11d57c175
1 structure Sqlcache :> SQLCACHE = struct
2
3 open Sql
4 open Mono
5
6 structure IS = IntBinarySet
7 structure IM = IntBinaryMap
8 structure StringKey = struct type ord_key = string val compare = String.compare end
9 structure SS = BinarySetFn (StringKey)
10 structure SM = BinaryMapFn (StringKey)
11 structure SIMM = MultimapFn (structure KeyMap = SM structure ValSet = IS)
12
13 val ffiIndices : int list ref = ref []
14
15 val rec tablesRead =
16 fn Query1 {From=tablePairs, ...} => SS.fromList (map #1 tablePairs)
17 | Union (q1,q2) => SS.union (tablesRead q1, tablesRead q2)
18
19 val tableWritten =
20 fn Insert (tab, _) => tab
21 | Delete (tab, _) => tab
22 | Update (tab, _, _) => tab
23
24 fun tablesInExp' exp' =
25 let
26 val nothing = {read = SS.empty, written = SS.empty}
27 in
28 case exp' of
29 EQuery {query=e, ...} =>
30 (case parse query e of
31 SOME q => {read = tablesRead q, written = SS.empty}
32 | NONE => nothing)
33 | EDml (e, _) =>
34 (case parse dml e of
35 SOME q => {read = SS.empty, written = SS.singleton (tableWritten q)}
36 | NONE => nothing)
37 | _ => nothing
38 end
39
40 val tablesInExp =
41 let
42 fun addTables (exp', {read, written}) =
43 let val {read = r, written = w} = tablesInExp' exp'
44 in {read = SS.union (r, read), written = SS.union (w, written)} end
45 in
46 MonoUtil.Exp.fold {typ = #2, exp = addTables}
47 {read = SS.empty, written = SS.empty}
48 end
49
50 fun intExp (n, loc) = (EPrim (Prim.Int (Int64.fromInt n)), loc)
51 fun intTyp loc = (TFfi ("Basis", "int"), loc)
52 fun boolPat (b, loc) = (PCon (Enum,
53 PConFfi {mod = "Basis", datatyp = "bool", arg = NONE,
54 con = if b then "True" else "False"},
55 NONE),
56 loc)
57 fun boolTyp loc = (TFfi ("Basis", "int"), loc)
58
59 fun ffiAppExp (module, func, index, loc) =
60 (EFfiApp (module, func ^ Int.toString index, []), loc)
61
62 fun sequence (befores, center, afters, loc) =
63 List.foldr (fn (exp, seq) => (ESeq (exp, seq), loc))
64 (List.foldl (fn (exp, seq) => (ESeq (seq, exp), loc))
65 center
66 afters)
67 befores
68
69 fun antiguardUnit (cond, exp, loc) =
70 (ECase (cond,
71 [(boolPat (false, loc), exp),
72 (boolPat (true, loc), (ERecord [], loc))],
73 {disc = boolTyp loc, result = (TRecord [], loc)}),
74 loc)
75
76 fun underAbs f (exp as (exp', loc)) =
77 case exp' of
78 EAbs (x, y, z, body) => (EAbs (x, y, z, underAbs f body), loc)
79 | _ => f exp
80
81 fun addCacheCheck (index, exp) =
82 let
83 fun f (body as (_, loc)) =
84 let
85 val check = ffiAppExp ("Cache", "check", index, loc)
86 val store = ffiAppExp ("Cache", "store", index, loc)
87 in
88 antiguardUnit (check, sequence ([], body, [store], loc), loc)
89 end
90 in
91 underAbs f exp
92 end
93
94 fun addCacheFlush (exp, tablesToIndices) =
95 let
96 fun addIndices (table, indices) = IS.union (indices, SIMM.find (tablesToIndices, table))
97 fun f (body as (_, loc)) =
98 let
99 fun mapFfi func = List.map (fn i => ffiAppExp ("Cache", func, i, loc))
100 val flushes =
101 IS.listItems (SS.foldr addIndices IS.empty (#written (tablesInExp body)))
102
103 in
104 sequence (mapFfi "flush" flushes, body, mapFfi "ready" flushes, loc)
105 end
106 in
107 underAbs f exp
108 end
109
110 val handlerIndices =
111 let
112 val isUnit =
113 fn (TRecord [], _) => true
114 | _ => false
115 fun maybeAdd (d, soFar as {readers, writers}) =
116 case d of
117 DExport (Link ReadOnly, _, name, typs, typ, _) =>
118 if List.all isUnit (typ::typs)
119 then {readers = IS.add (readers, name), writers = writers}
120 else soFar
121 | DExport (_, _, name, _, _, _) => (* Not read only. *)
122 {readers = readers, writers = IS.add (writers, name)}
123 | _ => soFar
124 in
125 MonoUtil.File.fold {typ = #2, exp = #2, decl = maybeAdd}
126 {readers = IS.empty, writers = IS.empty}
127 end
128
129 fun fileFoldMapiSelected f init (file, indices) =
130 let
131 fun doExp (original as ((a, index, b, exp, c), state)) =
132 if IS.member (indices, index)
133 then let val (newExp, newState) = f (index, exp, state)
134 in ((a, index, b, newExp, c), newState) end
135 else original
136 fun doDecl decl state =
137 let
138 val result =
139 case decl of
140 DVal x =>
141 let val (y, newState) = doExp (x, state)
142 in (DVal y, newState) end
143 | DValRec xs =>
144 let val (ys, newState) = ListUtil.foldlMap doExp state xs
145 in (DValRec ys, newState) end
146 | _ => (decl, state)
147 in
148 Search.Continue result
149 end
150 fun nada x y = Search.Continue (x, y)
151 in
152 case MonoUtil.File.mapfold {typ = nada, exp = nada, decl = doDecl} file init of
153 Search.Continue x => x
154 | _ => (file, init) (* Should never happen. *)
155 end
156
157 fun fileMapSelected f = #1 o fileFoldMapiSelected (fn (_, x, _) => (f x, ())) ()
158
159 val addCacheChecking =
160 let
161 fun f (index, exp, tablesToIndices) =
162 (addCacheCheck (index, exp),
163 SS.foldr (fn (table, tsToIs) => SIMM.insert (tsToIs, table, index))
164 tablesToIndices
165 (#read (tablesInExp exp)))
166 in
167 fileFoldMapiSelected f (SM.empty)
168 end
169
170 fun addCacheFlushing (file, tablesToIndices, writers) =
171 fileMapSelected (fn exp => addCacheFlush (exp, tablesToIndices)) (file, writers)
172
173 fun go file =
174 let
175 val {readers, writers} = handlerIndices file
176 val (fileWithChecks, tablesToIndices) = addCacheChecking (file, readers)
177 in
178 ffiIndices := IS.listItems readers;
179 addCacheFlushing (fileWithChecks, tablesToIndices, writers)
180 end
181
182 end