comparison src/sqlcache.sml @ 2213:365727ff68f4

Complete overhaul: cache queries based on immediate query result, not eventual HTML output.
author Ziv Scully <ziv@mit.edu>
date Tue, 14 Oct 2014 18:05:09 -0400
parents 388ba4dc7c96
children 639e62ca2530
comparison
equal deleted inserted replaced
2212:388ba4dc7c96 2213:365727ff68f4
1 structure Sqlcache :> SQLCACHE = struct 1 structure Sqlcache (* :> SQLCACHE *) = struct
2 2
3 open Sql 3 open Sql
4 open Mono 4 open Mono
5 5
6 structure IS = IntBinarySet 6 structure IS = IntBinarySet
7 structure IM = IntBinaryMap 7 structure IM = IntBinaryMap
8 structure StringKey = struct type ord_key = string val compare = String.compare end 8 structure SK = struct type ord_key = string val compare = String.compare end
9 structure SS = BinarySetFn (StringKey) 9 structure SS = BinarySetFn(SK)
10 structure SM = BinaryMapFn (StringKey) 10 structure SM = BinaryMapFn(SK)
11 structure SIMM = MultimapFn (structure KeyMap = SM structure ValSet = IS) 11 structure SIMM = MultimapFn(structure KeyMap = SM structure ValSet = IS)
12 12
13 val ffiIndices : int list ref = ref [] 13 (* Filled in by cacheWrap during Sqlcache. *)
14 14 val ffiInfo : {index : int, params : int} list ref = ref []
15 (* Expression construction utilities. *) 15
16 fun getFfiInfo () = !ffiInfo
17
18 (* Program analysis. *)
19
20 val useInjIfPossible =
21 fn SqConst prim => Inj (EPrim (Prim.String (Prim.Normal, Prim.toString prim)), ErrorMsg.dummySpan)
22 | sqexp => sqexp
23
24 fun equalities (canonicalTable : string -> string) :
25 sqexp -> ((string * string) * Mono.exp) list option =
26 let
27 val rec eqs =
28 fn Binop (Exps f, e1, e2) =>
29 (* TODO: use a custom datatype in Exps instead of a function. *)
30 (case f (Var 1, Var 2) of
31 Reln (Eq, [Var 1, Var 2]) =>
32 let
33 val (e1', e2') = (useInjIfPossible e1, useInjIfPossible e2)
34 in
35 case (e1', e2') of
36 (Field (t, f), Inj i) => SOME [((canonicalTable t, f), i)]
37 | (Inj i, Field (t, f)) => SOME [((canonicalTable t, f), i)]
38 | _ => NONE
39 end
40 | _ => NONE)
41 | Binop (Props f, e1, e2) =>
42 (* TODO: use a custom datatype in Props instead of a function. *)
43 (case f (True, False) of
44 And (True, False) =>
45 (case (eqs e1, eqs e2) of
46 (SOME eqs1, SOME eqs2) => SOME (eqs1 @ eqs2)
47 | _ => NONE)
48 | _ => NONE)
49 | _ => NONE
50 in
51 eqs
52 end
53
54 val equalitiesQuery =
55 fn Query1 {From = tablePairs, Where = SOME exp, ...} =>
56 equalities
57 (* If we have [SELECT ... FROM T AS T' ...], use T, not T'. *)
58 (fn t =>
59 case List.find (fn (_, tAs) => t = tAs) tablePairs of
60 NONE => t
61 | SOME (tOrig, _) => tOrig)
62 exp
63 | Query1 {Where = NONE, ...} => SOME []
64 | _ => NONE
65
66 val equalitiesDml =
67 fn Insert (tab, eqs) => SOME (List.mapPartial
68 (fn (name, sqexp) =>
69 case useInjIfPossible sqexp of
70 Inj e => SOME ((tab, name), e)
71 | _ => NONE)
72 eqs)
73 | Delete (tab, exp) => equalities (fn _ => tab) exp
74 (* TODO: examine the updated values and not just the way they're filtered. *)
75 (* For example, UPDATE foo SET Id = 9001 WHERE Id = 42 should update both the
76 Id = 42 and Id = 9001 cache entries. Could also think of it as doing a
77 Delete immediately followed by an Insert. *)
78 | Update (tab, _, exp) => equalities (fn _ => tab) exp
79
80 val rec tablesQuery =
81 fn Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs)
82 | Union (q1, q2) => SS.union (tablesQuery q1, tablesQuery q2)
83
84 val tableDml =
85 fn Insert (tab, _) => tab
86 | Delete (tab, _) => tab
87 | Update (tab, _, _) => tab
88
89
90 (* Program instrumentation. *)
91
92 val stringTyp = (TFfi ("Basis", "string"), ErrorMsg.dummySpan)
93
94 val sequence =
95 fn (exp :: exps) =>
96 let
97 val loc = ErrorMsg.dummySpan
98 in
99 List.foldl (fn (e', seq) => ESeq ((seq, loc), (e', loc))) exp exps
100 end
101 | _ => raise Match
102
103 fun ffiAppCache' (func, index, args) : Mono.exp' =
104 EFfiApp ("Sqlcache", func ^ Int.toString index, args)
105
106 fun ffiAppCache (func, index, args) : Mono. exp =
107 (ffiAppCache' (func, index, args), ErrorMsg.dummySpan)
108
109 val varPrefix = "queryResult"
110
111 fun indexOfName varName =
112 if String.isPrefix varPrefix varName
113 then Int.fromString (String.extract (varName, String.size varPrefix, NONE))
114 else NONE
115
116 val incRels = MonoUtil.Exp.map {typ = fn x => x, exp = fn ERel n => ERel (n + 1) | x => x}
117
118 (* Filled in by instrumentQuery during Monoize, used during Sqlcache. *)
119 val urlifiedRel0s : Mono.exp IM.map ref = ref IM.empty
120
121 (* Used by Monoize. *)
122 val instrumentQuery =
123 let
124 val nextQuery = ref 0
125 fun iq (query, urlifiedRel0) =
126 case query of
127 (EQuery {state = typ, ...}, loc) =>
128 let
129 val i = !nextQuery before nextQuery := !nextQuery + 1
130 in
131 urlifiedRel0s := IM.insert (!urlifiedRel0s, i, urlifiedRel0);
132 (* ASK: name variables properly? *)
133 (ELet (varPrefix ^ Int.toString i, typ, query,
134 (* Uses a dummy FFI call to keep the urlified expression around, which
135 in turn keeps the declarations required for urlification safe from
136 MonoShake. The dummy call is removed during Sqlcache. *)
137 (* ASK: is there a better way? *)
138 (ESeq ((EFfiApp ("Sqlcache", "dummy", [(urlifiedRel0, stringTyp)]), loc),
139 (ERel 0, loc)),
140 loc)),
141 loc)
142 end
143 | _ => raise Match
144 in
145 iq
146 end
147
148 val gunk : ((string * string) * Mono.exp) list list ref = ref [[]]
149
150 fun cacheWrap (query, i, urlifiedRel0, eqs) =
151 case query of
152 (EQuery {state = typ, ...}, _) =>
153 let
154 val loc = ErrorMsg.dummySpan
155 (* TODO: deal with effectful injected expressions. *)
156 val args = (ffiInfo := {index = i, params = length eqs} :: !ffiInfo;
157 map (fn (_, e) => (e, stringTyp)) eqs) before gunk := eqs :: !gunk
158 val argsInc = map (fn (e, t) => (incRels e, t)) args
159 in
160 (ECase (ffiAppCache ("check", i, args),
161 [((PNone stringTyp, loc),
162 (ELet ("q", typ, query,
163 (ESeq (ffiAppCache ("store", i, (urlifiedRel0, stringTyp) :: argsInc),
164 (ERel 0, loc)),
165 loc)),
166 loc)),
167 ((PSome (stringTyp, (PVar ("hit", stringTyp), loc)), loc),
168 (* ASK: what does this bool do? *)
169 (EUnurlify ((ERel 0, loc), typ, false), loc))],
170 {disc = stringTyp, result = typ}),
171 loc)
172 end
173 | _ => raise Match
174
175 fun fileMapfold doExp file start =
176 case MonoUtil.File.mapfold {typ = Search.return2,
177 exp = fn x => (fn s => Search.Continue (doExp x s)),
178 decl = Search.return2} file start of
179 Search.Continue x => x
180 | Search.Return _ => raise Match
181
182 fun fileMap doExp file = #1 (fileMapfold (fn x => fn _ => (doExp x, ())) file ())
183
184 val addChecking =
185 let
186 fun doExp queryInfo =
187 fn e' as ELet (v, t, queryExp as (EQuery {query = queryText, ...}, _), body) =>
188 let
189 fun bind x f = Option.mapPartial f x
190 val attempt =
191 (* Ziv misses Haskell's do notation.... *)
192 bind (parse query queryText) (fn queryParsed =>
193 (Print.preface ("gunk> ", (MonoPrint.p_exp MonoEnv.empty queryExp));
194 bind (indexOfName v) (fn i =>
195 bind (equalitiesQuery queryParsed) (fn eqs =>
196 bind (IM.find (!urlifiedRel0s, i)) (fn urlifiedRel0 =>
197 SOME (ELet (v, t, cacheWrap (queryExp, i, urlifiedRel0, eqs), body),
198 SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, i))
199 queryInfo
200 (tablesQuery queryParsed)))))))
201 in
202 case attempt of
203 SOME pair => pair
204 | NONE => (e', queryInfo)
205 end
206 | ESeq ((EFfiApp ("Sqlcache", "dummy", _), _), (e', _)) => (e', queryInfo)
207 | e' => (e', queryInfo)
208 in
209 fn file => fileMapfold (fn exp => fn state => doExp state exp) file SIMM.empty
210 end
211
212 fun addFlushing (file, queryInfo) =
213 let
214 val allIndices : int list = SM.foldr (fn (x, acc) => IS.listItems x @ acc) [] queryInfo
215 fun flushes indices = map (fn i => ffiAppCache' ("flush", i, [])) indices
216 val doExp =
217 fn dmlExp as EDml (dmlText, _) =>
218 let
219 val indices =
220 case parse dml dmlText of
221 SOME dmlParsed => SIMM.findList (queryInfo, tableDml dmlParsed)
222 | NONE => allIndices
223 in
224 sequence (flushes indices @ [dmlExp])
225 end
226 | e' => e'
227 in
228 fileMap doExp file
229 end
230
231 fun go file =
232 let
233 val () = Sql.sqlcacheMode := true
234 in
235 addFlushing (addChecking file) before Sql.sqlcacheMode := false
236 end
237
238
239 (* BEGIN OLD
16 240
17 fun intExp (n, loc) = (EPrim (Prim.Int (Int64.fromInt n)), loc) 241 fun intExp (n, loc) = (EPrim (Prim.Int (Int64.fromInt n)), loc)
18 fun intTyp loc = (TFfi ("Basis", "int"), loc) 242 fun intTyp loc = (TFfi ("Basis", "int"), loc)
243 fun stringExp (s, loc) = (EPrim (Prim.String (Prim.Normal, s)), loc)
244
19 fun boolPat (b, loc) = (PCon (Enum, 245 fun boolPat (b, loc) = (PCon (Enum,
20 PConFfi {mod = "Basis", datatyp = "bool", arg = NONE, 246 PConFfi {mod = "Basis", datatyp = "bool", arg = NONE,
21 con = if b then "True" else "False"}, 247 con = if b then "True" else "False"},
22 NONE), 248 NONE),
23 loc) 249 loc)
24 fun boolTyp loc = (TFfi ("Basis", "int"), loc) 250 fun boolTyp loc = (TFfi ("Basis", "int"), loc)
25 251
26 fun ffiAppExp (module, func, index, loc) = 252 fun ffiAppExp (module, func, index, args, loc) =
27 (EFfiApp (module, func ^ Int.toString index, []), loc) 253 (EFfiApp (module, func ^ Int.toString index, args), loc)
28 254
29 fun sequence ((exp :: exps), loc) = 255 val sequence =
256 fn ((exp :: exps), loc) =>
30 List.foldl (fn (exp, seq) => (ESeq (seq, exp), loc)) exp exps 257 List.foldl (fn (exp, seq) => (ESeq (seq, exp), loc)) exp exps
258 | _ => raise Match
31 259
32 fun antiguardUnit (cond, exp, loc) = 260 fun antiguardUnit (cond, exp, loc) =
33 (ECase (cond, 261 (ECase (cond,
34 [(boolPat (false, loc), exp), 262 [(boolPat (false, loc), exp),
35 (boolPat (true, loc), (ERecord [], loc))], 263 (boolPat (true, loc), (ERecord [], loc))],
39 fun underAbs f (exp as (exp', loc)) = 267 fun underAbs f (exp as (exp', loc)) =
40 case exp' of 268 case exp' of
41 EAbs (x, y, z, body) => (EAbs (x, y, z, underAbs f body), loc) 269 EAbs (x, y, z, body) => (EAbs (x, y, z, underAbs f body), loc)
42 | _ => f exp 270 | _ => f exp
43 271
44 (* Program analysis and augmentation. *)
45 272
46 val rec tablesRead = 273 val rec tablesRead =
47 fn Query1 {From=tablePairs, ...} => SS.fromList (map #1 tablePairs) 274 fn Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs)
48 | Union (q1,q2) => SS.union (tablesRead q1, tablesRead q2) 275 | Union (q1, q2) => SS.union (tablesRead q1, tablesRead q2)
49 276
50 val tableWritten = 277 val tableWritten =
51 fn Insert (tab, _) => tab 278 fn Insert (tab, _) => tab
52 | Delete (tab, _) => tab 279 | Delete (tab, _) => tab
53 | Update (tab, _, _) => tab 280 | Update (tab, _, _) => tab
55 fun tablesInExp' exp' = 282 fun tablesInExp' exp' =
56 let 283 let
57 val nothing = {read = SS.empty, written = SS.empty} 284 val nothing = {read = SS.empty, written = SS.empty}
58 in 285 in
59 case exp' of 286 case exp' of
60 EQuery {query=e, ...} => 287 EQuery {query = e, ...} =>
61 (case parse query e of 288 (case parse query e of
62 SOME q => {read = tablesRead q, written = SS.empty} 289 SOME q => {read = tablesRead q, written = SS.empty}
63 | NONE => nothing) 290 | NONE => nothing)
64 | EDml (e, _) => 291 | EDml (e, _) =>
65 (case parse dml e of 292 (case parse dml e of
69 end 296 end
70 297
71 val tablesInExp = 298 val tablesInExp =
72 let 299 let
73 fun addTables (exp', {read, written}) = 300 fun addTables (exp', {read, written}) =
74 let val {read = r, written = w} = tablesInExp' exp' 301 let
75 in {read = SS.union (r, read), written = SS.union (w, written)} end 302 val {read = r, written = w} = tablesInExp' exp'
303 in
304 {read = SS.union (r, read), written = SS.union (w, written)}
305 end
76 in 306 in
77 MonoUtil.Exp.fold {typ = #2, exp = addTables} 307 MonoUtil.Exp.fold {typ = #2, exp = addTables}
78 {read = SS.empty, written = SS.empty} 308 {read = SS.empty, written = SS.empty}
79 end 309 end
80 310
148 end 378 end
149 fun nada x y = Search.Continue (x, y) 379 fun nada x y = Search.Continue (x, y)
150 in 380 in
151 case MonoUtil.File.mapfold {typ = nada, exp = nada, decl = doDecl} file init of 381 case MonoUtil.File.mapfold {typ = nada, exp = nada, decl = doDecl} file init of
152 Search.Continue x => x 382 Search.Continue x => x
153 | _ => (file, init) (* Should never happen. *) 383 | _ => raise Match (* Should never happen. *)
154 end 384 end
155 385
156 fun fileMapSelected f = #1 o fileFoldMapiSelected (fn (_, x, _) => (f x, ())) () 386 fun fileMapSelected f = #1 o fileFoldMapiSelected (fn (_, x, _) => (f x, ())) ()
157 387
158 val addCacheChecking = 388 val addCacheChecking =
176 in 406 in
177 ffiIndices := IS.listItems readers; 407 ffiIndices := IS.listItems readers;
178 addCacheFlushing (fileWithChecks, tablesToIndices, writers) 408 addCacheFlushing (fileWithChecks, tablesToIndices, writers)
179 end 409 end
180 410
411 END OLD *)
412
181 end 413 end