comparison src/sqlcache.sml @ 2216:70ec9bb337be

Progress towards invalidation based on equalities of fields.
author Ziv Scully <ziv@mit.edu>
date Mon, 10 Nov 2014 22:04:40 -0500
parents 639e62ca2530
children f7113855f3b7
comparison
equal deleted inserted replaced
2215:639e62ca2530 2216:70ec9bb337be
1 structure Sqlcache (* :> SQLCACHE *) = struct 1 structure Sqlcache (* :> SQLCACHE *) = struct
2 2
3 open Sql
4 open Mono 3 open Mono
5 4
6 structure IS = IntBinarySet 5 structure IS = IntBinarySet
7 structure IM = IntBinaryMap 6 structure IM = IntBinaryMap
8 structure SK = struct type ord_key = string val compare = String.compare end 7 structure SK = struct type ord_key = string val compare = String.compare end
9 structure SS = BinarySetFn(SK) 8 structure SS = BinarySetFn(SK)
10 structure SM = BinaryMapFn(SK) 9 structure SM = BinaryMapFn(SK)
11 structure SIMM = MultimapFn(structure KeyMap = SM structure ValSet = IS) 10 structure SIMM = MultimapFn(structure KeyMap = SM structure ValSet = IS)
12 11
13 (* Filled in by cacheWrap during Sqlcache. *) 12 (* Filled in by [cacheWrap] during [Sqlcache]. *)
14 val ffiInfo : {index : int, params : int} list ref = ref [] 13 val ffiInfo : {index : int, params : int} list ref = ref []
15 14
16 fun getFfiInfo () = !ffiInfo 15 fun getFfiInfo () = !ffiInfo
17 16
18 (* Some FFIs have writing as their only effect, which the caching records. *) 17 (* Some FFIs have writing as their only effect, which the caching records. *)
19 val ffiEffectful = 18 val ffiEffectful =
19 (* TODO: have this less hard-coded. *)
20 let 20 let
21 val fs = SS.fromList ["htmlifyInt_w", 21 val fs = SS.fromList ["htmlifyInt_w",
22 "htmlifyFloat_w", 22 "htmlifyFloat_w",
23 "htmlifyString_w", 23 "htmlifyString_w",
24 "htmlifyBool_w", 24 "htmlifyBool_w",
38 end 38 end
39 39
40 40
41 (* Effect analysis. *) 41 (* Effect analysis. *)
42 42
43 (* Makes an exception for EWrite (which is recorded when caching). *) 43 (* Makes an exception for [EWrite] (which is recorded when caching). *)
44 fun effectful doPrint (effs : IS.set) (inFunction : bool) (bound : int) : Mono.exp -> bool = 44 fun effectful doPrint (effs : IS.set) (inFunction : bool) (bound : int) : Mono.exp -> bool =
45 (* If result is true, expression is definitely effectful. If result is 45 (* If result is true, expression is definitely effectful. If result is
46 false, then expression is definitely not effectful if effs is fully 46 false, then expression is definitely not effectful if effs is fully
47 populated. The intended pattern is to use this a number of times equal 47 populated. The intended pattern is to use this a number of times equal
48 to the number of declarations in a file, Bellman-Ford style. *) 48 to the number of declarations in a file, Bellman-Ford style. *)
60 | ENamed name => if IS.member (effs, name) then tru "named" else false 60 | ENamed name => if IS.member (effs, name) then tru "named" else false
61 | ECon (_, _, NONE) => false 61 | ECon (_, _, NONE) => false
62 | ECon (_, _, SOME e) => eff e 62 | ECon (_, _, SOME e) => eff e
63 | ENone _ => false 63 | ENone _ => false
64 | ESome (_, e) => eff e 64 | ESome (_, e) => eff e
65 (* TODO: use FFI whitelist. *)
66 | EFfi (m, f) => if ffiEffectful (m, f) then tru "ffi" else false 65 | EFfi (m, f) => if ffiEffectful (m, f) then tru "ffi" else false
67 | EFfiApp (m, f, _) => if ffiEffectful (m, f) then tru "ffiapp" else false 66 | EFfiApp (m, f, _) => if ffiEffectful (m, f) then tru "ffiapp" else false
68 (* ASK: we're calling functions effectful if they have effects when 67 (* ASK: we're calling functions effectful if they have effects when
69 applied or if the function expressions themselves have effects. 68 applied or if the function expressions themselves have effects.
70 Is that okay? *) 69 Is that okay? *)
129 in 128 in
130 MonoUtil.File.fold {typ = #2, exp = #2, decl = doDecl} IS.empty 129 MonoUtil.File.fold {typ = #2, exp = #2, decl = doDecl} IS.empty
131 end 130 end
132 131
133 132
133 (* Boolean formula normalization. *)
134
135 datatype normalForm = Cnf | Dnf
136
137 datatype 'atom formula =
138 Atom of 'atom
139 | Negate of 'atom formula
140 | Combo of normalForm * 'atom formula list
141
142 val flipNf = fn Cnf => Dnf | Dnf => Cnf
143
144 fun bind xs f = List.concat (map f xs)
145
146 val rec cartesianProduct : 'a list list -> 'a list list =
147 fn [] => [[]]
148 | (xs :: xss) => bind (cartesianProduct xss)
149 (fn ys => bind xs (fn x => [x :: ys]))
150
151 fun normalize (negate : 'atom -> 'atom) (norm : normalForm) =
152 fn Atom x => [[x]]
153 | Negate f => map (map negate) (normalize negate (flipNf norm) f)
154 | Combo (n, fs) =>
155 let
156 val fss = bind fs (normalize negate n)
157 in
158 if n = norm then fss else cartesianProduct fss
159 end
160
161 fun mapFormula mf =
162 fn Atom x => Atom (mf x)
163 | Negate f => Negate (mapFormula mf f)
164 | Combo (n, fs) => Combo (n, map (mapFormula mf) fs)
165
166
134 (* SQL analysis. *) 167 (* SQL analysis. *)
135 168
136 val useInjIfPossible = 169 val rec chooseTwos : 'a list -> ('a * 'a) list =
137 fn SqConst prim => Inj (EPrim (Prim.String (Prim.Normal, Prim.toString prim)), 170 fn [] => []
138 ErrorMsg.dummySpan) 171 | x :: ys => map (fn y => (x, y)) ys @ chooseTwos ys
139 | sqexp => sqexp 172
140 173 datatype atomExp =
141 fun equalities (canonicalTable : string -> string) : 174 QueryArg of int
142 sqexp -> ((string * string) * Mono.exp) list option = 175 | DmlRel of int
143 let 176 | Prim of Prim.t
144 val rec eqs = 177 | Field of string * string
145 fn Binop (Exps f, e1, e2) => 178
146 (* TODO: use a custom datatype in Exps instead of a function. *) 179 structure AtomExpKey : ORD_KEY = struct
147 (case f (Var 1, Var 2) of 180
148 Reln (Eq, [Var 1, Var 2]) => 181 type ord_key = atomExp
149 let 182
150 val (e1', e2') = (useInjIfPossible e1, useInjIfPossible e2) 183 val compare =
151 in 184 fn (QueryArg n1, QueryArg n2) => Int.compare (n1, n2)
152 case (e1', e2') of 185 | (QueryArg _, _) => LESS
153 (Field (t, f), Inj i) => SOME [((canonicalTable t, f), i)] 186 | (_, QueryArg _) => GREATER
154 | (Inj i, Field (t, f)) => SOME [((canonicalTable t, f), i)] 187 | (DmlRel n1, DmlRel n2) => Int.compare (n1, n2)
155 | _ => NONE 188 | (DmlRel _, _) => LESS
156 end 189 | (_, DmlRel _) => GREATER
190 | (Prim p1, Prim p2) => Prim.compare (p1, p2)
191 | (Prim _, _) => LESS
192 | (_, Prim _) => GREATER
193 | (Field (t1, f1), Field (t2, f2)) => String.compare (t1 ^ "." ^ f1, t2 ^ "." ^ f2)
194
195 end
196
197 structure UF = UnionFindFn(AtomExpKey)
198
199 fun conflictMaps (fQuery : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula,
200 fDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula) =
201 let
202 val toKnownEquality =
203 (* [NONE] here means unkown. Anything that isn't a comparison between
204 two knowns shouldn't be used, and simply dropping unused terms is
205 okay in disjunctive normal form. *)
206 fn (Sql.Eq, SOME e1, SOME e2) => SOME (e1, e2)
207 | _ => NONE
208 val equivClasses : (Sql.cmp * atomExp option * atomExp option) list -> atomExp list list =
209 UF.classes
210 o List.foldl UF.union' UF.empty
211 o List.mapPartial toKnownEquality
212 fun addToEqs (eqs, n, e) =
213 case IM.find (eqs, n) of
214 (* Comparing to a constant seems better? *)
215 SOME (EPrim _) => eqs
216 | _ => IM.insert (eqs, n, e)
217 val accumulateEqs =
218 (* [NONE] means we have a contradiction. *)
219 fn (_, NONE) => NONE
220 | ((Prim p1, Prim p2), eqso) =>
221 (case Prim.compare (p1, p2) of
222 EQUAL => eqso
157 | _ => NONE) 223 | _ => NONE)
158 | Binop (Props f, e1, e2) => 224 | ((QueryArg n, Prim p), SOME eqs) => SOME (addToEqs (eqs, n, EPrim p))
159 (* TODO: use a custom datatype in Props instead of a function. *) 225 | ((QueryArg n, DmlRel r), SOME eqs) => SOME (addToEqs (eqs, n, ERel r))
160 (case f (True, False) of 226 | ((Prim p, QueryArg n), SOME eqs) => SOME (addToEqs (eqs, n, EPrim p))
161 And (True, False) => 227 | ((DmlRel r, QueryArg n), SOME eqs) => SOME (addToEqs (eqs, n, ERel r))
162 (case (eqs e1, eqs e2) of 228 (* TODO: deal with equalities involving just [DmlRel]s and [Prim]s. *)
163 (SOME eqs1, SOME eqs2) => SOME (eqs1 @ eqs2) 229 | (_, eqso) => eqso
164 | _ => NONE) 230 val eqsOfClass : atomExp list -> Mono.exp' IM.map option =
165 | _ => NONE) 231 List.foldl accumulateEqs (SOME IM.empty)
166 | _ => NONE 232 o chooseTwos
167 in 233 fun toAtomExps rel (cmp, e1, e2) =
168 eqs 234 let
169 end 235 val qa =
170 236 (* Here [NONE] means unkown. *)
171 val equalitiesQuery = 237 fn Sql.SqConst p => SOME (Prim p)
172 fn Query1 {From = tablePairs, Where = SOME exp, ...} => 238 | Sql.Field tf => SOME (Field tf)
173 equalities 239 | Sql.Inj (EPrim p, _) => SOME (Prim p)
174 (* If we have [SELECT ... FROM T AS T' ...], use T, not T'. *) 240 | Sql.Inj (ERel n, _) => SOME (rel n)
175 (fn t => 241 (* We can't deal with anything else. *)
176 case List.find (fn (_, tAs) => t = tAs) tablePairs of 242 | _ => NONE
177 NONE => t 243 in
178 | SOME (tOrig, _) => tOrig) 244 (cmp, qa e1, qa e2)
179 exp 245 end
180 | Query1 {Where = NONE, ...} => SOME [] 246 fun negateCmp (cmp, e1, e2) =
181 | _ => NONE 247 (case cmp of
182 248 Sql.Eq => Sql.Ne
183 val equalitiesDml = 249 | Sql.Ne => Sql.Eq
184 fn Insert (tab, eqs) => SOME (List.mapPartial 250 | Sql.Lt => Sql.Ge
185 (fn (name, sqexp) => 251 | Sql.Le => Sql.Gt
186 case useInjIfPossible sqexp of 252 | Sql.Gt => Sql.Le
187 Inj e => SOME ((tab, name), e) 253 | Sql.Ge => Sql.Lt,
188 | _ => NONE) 254 e1, e2)
189 eqs) 255 val markQuery = mapFormula (toAtomExps QueryArg)
190 | Delete (tab, exp) => equalities (fn _ => tab) exp 256 val markDml = mapFormula (toAtomExps DmlRel)
191 (* TODO: examine the updated values and not just the way they're filtered. *) 257 val dnf = normalize negateCmp Dnf (Combo (Cnf, [markQuery fQuery, markDml fDml]))
192 (* For example, UPDATE foo SET Id = 9001 WHERE Id = 42 should update both the 258 (* If one of the terms in a conjunction leads to a contradiction, which
193 Id = 42 and Id = 9001 cache entries. Could also think of it as doing a 259 is represented by [NONE], drop the entire conjunction. *)
194 Delete immediately followed by an Insert. *) 260 val sequenceOption = List.foldr (fn (SOME x, SOME xs) => SOME (x :: xs) | _ => NONE)
195 | Update (tab, _, exp) => equalities (fn _ => tab) exp 261 (SOME [])
262 in
263 List.mapPartial (sequenceOption o map eqsOfClass o equivClasses) dnf
264 end
265
266 val rec sqexpToFormula =
267 fn Sql.SqTrue => Combo (Cnf, [])
268 | Sql.SqFalse => Combo (Dnf, [])
269 | Sql.SqNot e => Negate (sqexpToFormula e)
270 | Sql.Binop (Sql.RCmp c, e1, e2) => Atom (c, e1, e2)
271 | Sql.Binop (Sql.RLop l, p1, p2) => Combo (case l of Sql.And => Cnf | Sql.Or => Dnf,
272 [sqexpToFormula p1, sqexpToFormula p2])
273 (* ASK: any other sqexps that can be props? *)
274 | _ => raise Match
275
276 val rec queryToFormula =
277 fn Sql.Query1 {From = tablePairs, Where = NONE, ...} => Combo (Cnf, [])
278 | Sql.Query1 {From = tablePairs, Where = SOME e, ...} =>
279 let
280 fun renameString table =
281 case List.find (fn (_, t) => table = t) tablePairs of
282 NONE => table
283 | SOME (realTable, _) => realTable
284 val renameSqexp =
285 fn Sql.Field (table, field) => Sql.Field (renameString table, field)
286 | e => e
287 fun renameAtom (cmp, e1, e2) = (cmp, renameSqexp e1, renameSqexp e2)
288 in
289 mapFormula renameAtom (sqexpToFormula e)
290 end
291 | Sql.Union (q1, q2) => Combo (Dnf, [queryToFormula q1, queryToFormula q2])
292
293 val rec dmlToFormula =
294 fn Sql.Insert (table, vals) =>
295 Combo (Cnf, map (fn (field, v) => Atom (Sql.Eq, Sql.Field (table, field), v)) vals)
296 | Sql.Delete (_, wher) => sqexpToFormula wher
297 (* TODO: refine formula for the vals part, which could take into account the wher part. *)
298 | Sql.Update (table, vals, wher) => Combo (Dnf, [dmlToFormula (Sql.Insert (table, vals)),
299 dmlToFormula (Sql.Delete (table, wher))])
196 300
197 val rec tablesQuery = 301 val rec tablesQuery =
198 fn Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs) 302 fn Sql.Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs)
199 | Union (q1, q2) => SS.union (tablesQuery q1, tablesQuery q2) 303 | Sql.Union (q1, q2) => SS.union (tablesQuery q1, tablesQuery q2)
200 304
201 val tableDml = 305 val tableDml =
202 fn Insert (tab, _) => tab 306 fn Sql.Insert (tab, _) => tab
203 | Delete (tab, _) => tab 307 | Sql.Delete (tab, _) => tab
204 | Update (tab, _, _) => tab 308 | Sql.Update (tab, _, _) => tab
205 309
206 310
207 (* Program instrumentation. *) 311 (* Program instrumentation. *)
208 312
209 fun stringExp s = (EPrim (Prim.String (Prim.Normal, s)), ErrorMsg.dummySpan) 313 fun stringExp s = (EPrim (Prim.String (Prim.Normal, s)), ErrorMsg.dummySpan)
314
210 val stringTyp = (TFfi ("Basis", "string"), ErrorMsg.dummySpan) 315 val stringTyp = (TFfi ("Basis", "string"), ErrorMsg.dummySpan)
211 316
212 val sequence = 317 val sequence =
213 fn (exp :: exps) => 318 fn (exp :: exps) =>
214 let 319 let
241 bind = fn (level, MonoUtil.Exp.RelE _) => level + 1 | (level, _) => level} 346 bind = fn (level, MonoUtil.Exp.RelE _) => level + 1 | (level, _) => level}
242 bound 347 bound
243 348
244 val incRels = incRelsBound 0 349 val incRels = incRelsBound 0
245 350
246 (* Filled in by instrumentQuery during Monoize, used during Sqlcache. *) 351 (* Filled in by instrumentQuery during [Monoize], used during [Sqlcache]. *)
247 val urlifiedRel0s : Mono.exp IM.map ref = ref IM.empty 352 val urlifiedRel0s : Mono.exp IM.map ref = ref IM.empty
248 353
249 (* Used by Monoize. *) 354 (* Used by [Monoize]. *)
250 val instrumentQuery = 355 val instrumentQuery =
251 let 356 let
252 val nextQuery = ref 0 357 val nextQuery = ref 0
253 fun iq (query, urlifiedRel0) = 358 fun iq (query, urlifiedRel0) =
254 case query of 359 case query of
258 in 363 in
259 urlifiedRel0s := IM.insert (!urlifiedRel0s, i, urlifiedRel0); 364 urlifiedRel0s := IM.insert (!urlifiedRel0s, i, urlifiedRel0);
260 (ELet (varPrefix ^ Int.toString i, typ, query, 365 (ELet (varPrefix ^ Int.toString i, typ, query,
261 (* Uses a dummy FFI call to keep the urlified expression around, which 366 (* Uses a dummy FFI call to keep the urlified expression around, which
262 in turn keeps the declarations required for urlification safe from 367 in turn keeps the declarations required for urlification safe from
263 MonoShake. The dummy call is removed during Sqlcache. *) 368 [MonoShake]. The dummy call is removed during [Sqlcache]. *)
264 (* TODO: thread a Monoize.Fm.t through this module. *) 369 (* TODO: thread a [Monoize.Fm.t] through this module. *)
265 (ESeq ((EFfiApp ("Sqlcache", "dummy", [(urlifiedRel0, stringTyp)]), loc), 370 (ESeq ((EFfiApp ("Sqlcache",
371 "dummy",
372 [(urlifiedRel0, stringTyp)]),
373 loc),
266 (ERel 0, loc)), 374 (ERel 0, loc)),
267 loc)), 375 loc)),
268 loc) 376 loc)
269 end 377 end
270 | _ => raise Match 378 | _ => raise Match
271 in 379 in
272 iq 380 iq
273 end 381 end
274 382
275 fun cacheWrap (query, i, urlifiedRel0, eqs) = 383 fun cacheWrap (query, i, urlifiedRel0, args) =
276 case query of 384 case query of
277 (EQuery {state = typ, ...}, _) => 385 (EQuery {state = typ, ...}, _) =>
278 let 386 let
279 val () = ffiInfo := {index = i, params = length eqs} :: !ffiInfo 387 val () = ffiInfo := {index = i, params = length args} :: !ffiInfo
280 val loc = ErrorMsg.dummySpan 388 val loc = ErrorMsg.dummySpan
281 (* We ensure before this step that all arguments aren't effectful. 389 (* We ensure before this step that all arguments aren't effectful.
282 by turning them into local variables as needed. *) 390 by turning them into local variables as needed. *)
283 val args = map (fn (_, e) => (e, stringTyp)) eqs 391 val argTyps = map (fn e => (e, stringTyp)) args
284 val argsInc = map (fn (e, typ) => (incRels 1 e, typ)) args 392 val argTypsInc = map (fn (e, typ) => (incRels 1 e, typ)) argTyps
285 val check = ffiAppCache ("check", i, args) 393 val check = ffiAppCache ("check", i, argTyps)
286 val store = ffiAppCache ("store", i, (urlifiedRel0, stringTyp) :: argsInc) 394 val store = ffiAppCache ("store", i, (urlifiedRel0, stringTyp) :: argTypsInc)
287 val rel0 = (ERel 0, loc) 395 val rel0 = (ERel 0, loc)
288 in 396 in
289 (ECase (check, 397 (ECase (check,
290 [((PNone stringTyp, loc), 398 [((PNone stringTyp, loc),
291 (ELet ("q", typ, query, (ESeq (store, rel0), loc)), loc)), 399 (ELet ("q", typ, query, (ESeq (store, rel0), loc)), loc)),
313 queryExp' as (EQuery {query = origQueryText, 421 queryExp' as (EQuery {query = origQueryText,
314 initial, body, state, tables, exps}, queryLoc), 422 initial, body, state, tables, exps}, queryLoc),
315 letBody) => 423 letBody) =>
316 let 424 let
317 val loc = ErrorMsg.dummySpan 425 val loc = ErrorMsg.dummySpan
318 val chunks = chunkify origQueryText 426 val chunks = Sql.chunkify origQueryText
319 fun strcat (e1, e2) = (EStrcat (e1, e2), loc) 427 fun strcat (e1, e2) = (EStrcat (e1, e2), loc)
320 val (newQueryText, newVariables) = 428 val (newQueryText, newVariables) =
321 (* Important that this is foldr (to oppose foldl below). *) 429 (* Important that this is foldr (to oppose foldl below). *)
322 List.foldr 430 List.foldr
323 (fn (chunk, (qText, newVars)) => 431 (fn (chunk, (qText, newVars)) =>
432 (* Variable bound to the head of newBs will have the lowest index. *)
324 case chunk of 433 case chunk of
325 Exp (e as (EPrim _, _)) => (strcat (e, qText), newVars) 434 Sql.Exp (e as (EPrim _, _)) => (strcat (e, qText), newVars)
326 | Exp (e as (ERel _, _)) => (strcat (e, qText), newVars) 435 | Sql.Exp e =>
327 | Exp (e as (ENamed _, _)) => (strcat (e, qText), newVars)
328 (* Head of newVars has lowest index. *)
329 | Exp e =>
330 let 436 let
331 val n = length newVars 437 val n = length newVars
332 in 438 in
333 (* This is the (n + 1)th new variable, so 439 (* This is the (n + 1)th new variable, so
334 there are already n new variables bound, 440 there are already n new variables bound,
335 so we increment indices by n. *) 441 so we increment indices by n. *)
336 (strcat ((ERel (~(n+1)), loc), qText), incRels n e :: newVars) 442 (strcat ((ERel (~(n+1)), loc), qText), incRels n e :: newVars)
337 end 443 end
338 | String s => (strcat (stringExp s, qText), newVars)) 444 | Sql.String s => (strcat (stringExp s, qText), newVars))
339 (stringExp "", []) 445 (stringExp "", [])
340 chunks 446 chunks
341 fun wrapLets e' = 447 fun wrapLets e' =
342 (* Important that this is foldl (to oppose foldr above). *) 448 (* Important that this is foldl (to oppose foldr above). *)
343 List.foldl (fn (v, e') => ELet ("sqlArgz", stringTyp, v, (e', loc))) e' newVariables 449 List.foldl (fn (v, e') => ELet ("sqlArgz", stringTyp, v, (e', loc)))
450 e'
451 newVariables
452 val numArgs = length newVariables
344 (* Increment once for each new variable just made. *) 453 (* Increment once for each new variable just made. *)
345 val queryExp = incRels (length newVariables) 454 val queryExp = incRels (length newVariables)
346 (EQuery {query = newQueryText, 455 (EQuery {query = newQueryText,
347 initial = initial, 456 initial = initial,
348 body = body, 457 body = body,
350 tables = tables, 459 tables = tables,
351 exps = exps}, 460 exps = exps},
352 queryLoc) 461 queryLoc)
353 val (EQuery {query = queryText, ...}, _) = queryExp 462 val (EQuery {query = queryText, ...}, _) = queryExp
354 (* val () = Print.preface ("sqlcache> ", (MonoPrint.p_exp MonoEnv.empty queryText)); *) 463 (* val () = Print.preface ("sqlcache> ", (MonoPrint.p_exp MonoEnv.empty queryText)); *)
464 val args = List.tabulate (numArgs, fn n => (ERel n, loc))
355 fun bind x f = Option.mapPartial f x 465 fun bind x f = Option.mapPartial f x
356 fun guard b x = if b then x else NONE 466 fun guard b x = if b then x else NONE
357 (* DEBUG: set first boolean argument to true to turn on printing. *) 467 (* DEBUG: set first boolean argument to true to turn on printing. *)
358 fun safe bound = not o effectful true (effectfulMap file) false bound 468 fun safe bound = not o effectful true (effectfulMap file) false bound
359 val attempt = 469 val attempt =
360 (* Ziv misses Haskell's do notation.... *) 470 (* Ziv misses Haskell's do notation.... *)
361 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) ( 471 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) (
362 bind (parse query queryText) (fn queryParsed => 472 bind (Sql.parse Sql.query queryText) (fn queryParsed =>
363 bind (indexOfName v) (fn i => 473 bind (indexOfName v) (fn i =>
364 bind (equalitiesQuery queryParsed) (fn eqs =>
365 bind (IM.find (!urlifiedRel0s, i)) (fn urlifiedRel0 => 474 bind (IM.find (!urlifiedRel0s, i)) (fn urlifiedRel0 =>
366 SOME (wrapLets (ELet (v, t, 475 SOME (wrapLets (ELet (v, t,
367 cacheWrap (queryExp, i, urlifiedRel0, eqs), 476 cacheWrap (queryExp, i, urlifiedRel0, args),
368 incRelsBound 1 (length newVariables) letBody)), 477 incRelsBound 1 (length newVariables) letBody)),
369 SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, i)) 478 SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, i))
370 queryInfo 479 queryInfo
371 (tablesQuery queryParsed))))))) 480 (tablesQuery queryParsed))))))
372 in 481 in
373 case attempt of 482 case attempt of
374 SOME pair => pair 483 SOME pair => pair
375 | NONE => (e', queryInfo) 484 | NONE => (e', queryInfo)
376 end 485 end
378 | e' => (e', queryInfo) 487 | e' => (e', queryInfo)
379 in 488 in
380 fileMapfold (fn exp => fn state => doExp state exp) file SIMM.empty 489 fileMapfold (fn exp => fn state => doExp state exp) file SIMM.empty
381 end 490 end
382 491
492 fun invalidations (nQueryArgs, query, dml) =
493 let
494 val loc = ErrorMsg.dummySpan
495 val optionToExp =
496 fn NONE => (ENone stringTyp, loc)
497 | SOME e => (ESome (stringTyp, (e, loc)), loc)
498 fun eqsToInvalidation eqs =
499 let
500 fun inv n = if n < 0 then [] else optionToExp (IM.find (eqs, n)) :: inv (n - 1)
501 in
502 inv (nQueryArgs - 1)
503 end
504 in
505 map (map eqsToInvalidation) (conflictMaps (queryToFormula query, dmlToFormula dml))
506 end
507
383 fun addFlushing (file, queryInfo) = 508 fun addFlushing (file, queryInfo) =
384 let 509 let
385 val allIndices : int list = SM.foldr (fn (x, acc) => IS.listItems x @ acc) [] queryInfo 510 val allIndices : int list = SM.foldr (fn (x, acc) => IS.listItems x @ acc) [] queryInfo
386 fun flushes indices = map (fn i => ffiAppCache' ("flush", i, [])) indices 511 fun flushes indices = map (fn i => ffiAppCache' ("flush", i, [])) indices
387 val doExp = 512 val doExp =
388 fn dmlExp as EDml (dmlText, _) => 513 fn dmlExp as EDml (dmlText, _) =>
389 let 514 let
390 val indices = 515 val indices =
391 case parse dml dmlText of 516 case Sql.parse Sql.dml dmlText of
392 SOME dmlParsed => SIMM.findList (queryInfo, tableDml dmlParsed) 517 SOME dmlParsed => SIMM.findList (queryInfo, tableDml dmlParsed)
393 | NONE => allIndices 518 | NONE => allIndices
394 in 519 in
395 sequence (flushes indices @ [dmlExp]) 520 sequence (flushes indices @ [dmlExp])
396 end 521 end
406 val () = Sql.sqlcacheMode := false 531 val () = Sql.sqlcacheMode := false
407 in 532 in
408 file' 533 file'
409 end 534 end
410 535
411
412 (* BEGIN OLD
413
414 fun intExp (n, loc) = (EPrim (Prim.Int (Int64.fromInt n)), loc)
415 fun intTyp loc = (TFfi ("Basis", "int"), loc)
416 fun stringExp (s, loc) = (EPrim (Prim.String (Prim.Normal, s)), loc)
417
418 fun boolPat (b, loc) = (PCon (Enum,
419 PConFfi {mod = "Basis", datatyp = "bool", arg = NONE,
420 con = if b then "True" else "False"},
421 NONE),
422 loc)
423 fun boolTyp loc = (TFfi ("Basis", "int"), loc)
424
425 fun ffiAppExp (module, func, index, args, loc) =
426 (EFfiApp (module, func ^ Int.toString index, args), loc)
427
428 val sequence =
429 fn ((exp :: exps), loc) =>
430 List.foldl (fn (exp, seq) => (ESeq (seq, exp), loc)) exp exps
431 | _ => raise Match
432
433 fun antiguardUnit (cond, exp, loc) =
434 (ECase (cond,
435 [(boolPat (false, loc), exp),
436 (boolPat (true, loc), (ERecord [], loc))],
437 {disc = boolTyp loc, result = (TRecord [], loc)}),
438 loc)
439
440 fun underAbs f (exp as (exp', loc)) =
441 case exp' of
442 EAbs (x, y, z, body) => (EAbs (x, y, z, underAbs f body), loc)
443 | _ => f exp
444
445
446 val rec tablesRead =
447 fn Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs)
448 | Union (q1, q2) => SS.union (tablesRead q1, tablesRead q2)
449
450 val tableWritten =
451 fn Insert (tab, _) => tab
452 | Delete (tab, _) => tab
453 | Update (tab, _, _) => tab
454
455 fun tablesInExp' exp' =
456 let
457 val nothing = {read = SS.empty, written = SS.empty}
458 in
459 case exp' of
460 EQuery {query = e, ...} =>
461 (case parse query e of
462 SOME q => {read = tablesRead q, written = SS.empty}
463 | NONE => nothing)
464 | EDml (e, _) =>
465 (case parse dml e of
466 SOME q => {read = SS.empty, written = SS.singleton (tableWritten q)}
467 | NONE => nothing)
468 | _ => nothing
469 end
470
471 val tablesInExp =
472 let
473 fun addTables (exp', {read, written}) =
474 let
475 val {read = r, written = w} = tablesInExp' exp'
476 in
477 {read = SS.union (r, read), written = SS.union (w, written)}
478 end
479 in
480 MonoUtil.Exp.fold {typ = #2, exp = addTables}
481 {read = SS.empty, written = SS.empty}
482 end
483
484 fun addCacheCheck (index, exp) =
485 let
486 fun f (body as (_, loc)) =
487 let
488 val check = ffiAppExp ("Cache", "check", index, loc)
489 val store = ffiAppExp ("Cache", "store", index, loc)
490 in
491 antiguardUnit (check, sequence ([body, store], loc), loc)
492 end
493 in
494 underAbs f exp
495 end
496
497 fun addCacheFlush (exp, tablesToIndices) =
498 let
499 fun addIndices (table, indices) = IS.union (indices, SIMM.find (tablesToIndices, table))
500 fun f (body as (_, loc)) =
501 let
502 fun mapFfi func = List.map (fn i => ffiAppExp ("Cache", func, i, loc))
503 val flushes =
504 IS.listItems (SS.foldr addIndices IS.empty (#written (tablesInExp body)))
505 in
506 sequence (mapFfi "flush" flushes @ [body] @ mapFfi "ready" flushes, loc)
507 end
508 in
509 underAbs f exp
510 end
511
512 val handlerIndices =
513 let
514 val isUnit =
515 fn (TRecord [], _) => true
516 | _ => false
517 fun maybeAdd (d, soFar as {readers, writers}) =
518 case d of
519 DExport (Link ReadOnly, _, name, typs, typ, _) =>
520 if List.all isUnit (typ::typs)
521 then {readers = IS.add (readers, name), writers = writers}
522 else soFar
523 | DExport (_, _, name, _, _, _) => (* Not read only. *)
524 {readers = readers, writers = IS.add (writers, name)}
525 | _ => soFar
526 in
527 MonoUtil.File.fold {typ = #2, exp = #2, decl = maybeAdd}
528 {readers = IS.empty, writers = IS.empty}
529 end
530
531 fun fileFoldMapiSelected f init (file, indices) =
532 let
533 fun doExp (original as ((a, index, b, exp, c), state)) =
534 if IS.member (indices, index)
535 then let val (newExp, newState) = f (index, exp, state)
536 in ((a, index, b, newExp, c), newState) end
537 else original
538 fun doDecl decl state =
539 let
540 val result =
541 case decl of
542 DVal x =>
543 let val (y, newState) = doExp (x, state)
544 in (DVal y, newState) end
545 | DValRec xs =>
546 let val (ys, newState) = ListUtil.foldlMap doExp state xs
547 in (DValRec ys, newState) end
548 | _ => (decl, state)
549 in
550 Search.Continue result
551 end
552 fun nada x y = Search.Continue (x, y)
553 in
554 case MonoUtil.File.mapfold {typ = nada, exp = nada, decl = doDecl} file init of
555 Search.Continue x => x
556 | _ => raise Match (* Should never happen. *)
557 end
558
559 fun fileMapSelected f = #1 o fileFoldMapiSelected (fn (_, x, _) => (f x, ())) ()
560
561 val addCacheChecking =
562 let
563 fun f (index, exp, tablesToIndices) =
564 (addCacheCheck (index, exp),
565 SS.foldr (fn (table, tsToIs) => SIMM.insert (tsToIs, table, index))
566 tablesToIndices
567 (#read (tablesInExp exp)))
568 in
569 fileFoldMapiSelected f (SM.empty)
570 end
571
572 fun addCacheFlushing (file, tablesToIndices, writers) =
573 fileMapSelected (fn exp => addCacheFlush (exp, tablesToIndices)) (file, writers)
574
575 fun go file =
576 let
577 val {readers, writers} = handlerIndices file
578 val (fileWithChecks, tablesToIndices) = addCacheChecking (file, readers)
579 in
580 ffiIndices := IS.listItems readers;
581 addCacheFlushing (fileWithChecks, tablesToIndices, writers)
582 end
583
584 END OLD *)
585
586 end 536 end