comparison src/sqlcache.sml @ 2218:f7113855f3b7

More invalidation progress.
author Ziv Scully <ziv@mit.edu>
date Tue, 11 Nov 2014 04:25:20 -0500
parents 70ec9bb337be
children ff38b3e0cdfd
comparison
equal deleted inserted replaced
2217:98b87d905601 2218:f7113855f3b7
146 val rec cartesianProduct : 'a list list -> 'a list list = 146 val rec cartesianProduct : 'a list list -> 'a list list =
147 fn [] => [[]] 147 fn [] => [[]]
148 | (xs :: xss) => bind (cartesianProduct xss) 148 | (xs :: xss) => bind (cartesianProduct xss)
149 (fn ys => bind xs (fn x => [x :: ys])) 149 (fn ys => bind xs (fn x => [x :: ys]))
150 150
151 fun normalize (negate : 'atom -> 'atom) (norm : normalForm) = 151 (* Pushes all negation to the atoms.*)
152 fun pushNegate (negate : 'atom -> 'atom) (negating : bool) =
153 fn Atom x => Atom (if negating then negate x else x)
154 | Negate f => pushNegate negate (not negating) f
155 | Combo (n, fs) => Combo (if negating then flipNf n else n, map (pushNegate negate negating) fs)
156
157 val rec flatten =
158 fn Combo (n, fs) =>
159 Combo (n, List.foldr (fn (f, acc) =>
160 case f of
161 Combo (n', fs') => if n = n' then fs' @ acc else f :: acc
162 | _ => f :: acc)
163 []
164 (map flatten fs))
165 | f => f
166
167 fun normalize' (negate : 'atom -> 'atom) (norm : normalForm) =
152 fn Atom x => [[x]] 168 fn Atom x => [[x]]
153 | Negate f => map (map negate) (normalize negate (flipNf norm) f) 169 | Negate f => map (map negate) (normalize' negate (flipNf norm) f)
154 | Combo (n, fs) => 170 | Combo (n, fs) =>
155 let 171 let
156 val fss = bind fs (normalize negate n) 172 val fss = bind fs (normalize' negate n)
157 in 173 in
158 if n = norm then fss else cartesianProduct fss 174 if n = norm then fss else cartesianProduct fss
159 end 175 end
160 176
161 fun mapFormula mf = 177 fun normalize negate norm = normalize' negate norm o flatten o pushNegate negate false
162 fn Atom x => Atom (mf x) 178
163 | Negate f => Negate (mapFormula mf f) 179 fun mapFormulaSigned positive mf =
164 | Combo (n, fs) => Combo (n, map (mapFormula mf) fs) 180 fn Atom x => Atom (mf (positive, x))
165 181 | Negate f => Negate (mapFormulaSigned (not positive) mf f)
182 | Combo (n, fs) => Combo (n, map (mapFormulaSigned positive mf) fs)
183
184 fun mapFormula mf = mapFormulaSigned true (fn (_, x) => mf x)
166 185
167 (* SQL analysis. *) 186 (* SQL analysis. *)
168 187
169 val rec chooseTwos : 'a list -> ('a * 'a) list = 188 val rec chooseTwos : 'a list -> ('a * 'a) list =
170 fn [] => [] 189 fn [] => []
173 datatype atomExp = 192 datatype atomExp =
174 QueryArg of int 193 QueryArg of int
175 | DmlRel of int 194 | DmlRel of int
176 | Prim of Prim.t 195 | Prim of Prim.t
177 | Field of string * string 196 | Field of string * string
197
198 val equalAtomExp =
199 let
200 val isEqual = fn EQUAL => true | _ => false
201 in
202 fn (QueryArg n1, QueryArg n2) => n1 = n2
203 | (DmlRel n1, DmlRel n2) => n1 = n2
204 | (Prim p1, Prim p2) => isEqual (Prim.compare (p1, p2))
205 | (Field (t1, f1), Field (t2, f2)) => isEqual (String.compare (t1 ^ "." ^ f1, t2 ^ "." ^ f2))
206 | _ => false
207 end
178 208
179 structure AtomExpKey : ORD_KEY = struct 209 structure AtomExpKey : ORD_KEY = struct
180 210
181 type ord_key = atomExp 211 type ord_key = atomExp
182 212
194 224
195 end 225 end
196 226
197 structure UF = UnionFindFn(AtomExpKey) 227 structure UF = UnionFindFn(AtomExpKey)
198 228
199 fun conflictMaps (fQuery : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula, 229 (* val conflictMaps : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula *)
200 fDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula) = 230 (* * (Sql.cmp * Sql.sqexp * Sql.sqexp) formula *)
201 let 231 (* -> Mono.exp' IM.map list = *)
232 (* let *)
202 val toKnownEquality = 233 val toKnownEquality =
203 (* [NONE] here means unkown. Anything that isn't a comparison between 234 (* [NONE] here means unkown. Anything that isn't a comparison between
204 two knowns shouldn't be used, and simply dropping unused terms is 235 two knowns shouldn't be used, and simply dropping unused terms is
205 okay in disjunctive normal form. *) 236 okay in disjunctive normal form. *)
206 fn (Sql.Eq, SOME e1, SOME e2) => SOME (e1, e2) 237 fn (Sql.Eq, SOME e1, SOME e2) => SOME (e1, e2)
210 o List.foldl UF.union' UF.empty 241 o List.foldl UF.union' UF.empty
211 o List.mapPartial toKnownEquality 242 o List.mapPartial toKnownEquality
212 fun addToEqs (eqs, n, e) = 243 fun addToEqs (eqs, n, e) =
213 case IM.find (eqs, n) of 244 case IM.find (eqs, n) of
214 (* Comparing to a constant seems better? *) 245 (* Comparing to a constant seems better? *)
215 SOME (EPrim _) => eqs 246 SOME (Prim _) => eqs
216 | _ => IM.insert (eqs, n, e) 247 | _ => IM.insert (eqs, n, e)
217 val accumulateEqs = 248 val accumulateEqs =
218 (* [NONE] means we have a contradiction. *) 249 (* [NONE] means we have a contradiction. *)
219 fn (_, NONE) => NONE 250 fn (_, NONE) => NONE
220 | ((Prim p1, Prim p2), eqso) => 251 | ((Prim p1, Prim p2), eqso) =>
221 (case Prim.compare (p1, p2) of 252 (case Prim.compare (p1, p2) of
222 EQUAL => eqso 253 EQUAL => eqso
223 | _ => NONE) 254 | _ => NONE)
224 | ((QueryArg n, Prim p), SOME eqs) => SOME (addToEqs (eqs, n, EPrim p)) 255 | ((QueryArg n, Prim p), SOME eqs) => SOME (addToEqs (eqs, n, Prim p))
225 | ((QueryArg n, DmlRel r), SOME eqs) => SOME (addToEqs (eqs, n, ERel r)) 256 | ((QueryArg n, DmlRel r), SOME eqs) => SOME (addToEqs (eqs, n, DmlRel r))
226 | ((Prim p, QueryArg n), SOME eqs) => SOME (addToEqs (eqs, n, EPrim p)) 257 | ((Prim p, QueryArg n), SOME eqs) => SOME (addToEqs (eqs, n, Prim p))
227 | ((DmlRel r, QueryArg n), SOME eqs) => SOME (addToEqs (eqs, n, ERel r)) 258 | ((DmlRel r, QueryArg n), SOME eqs) => SOME (addToEqs (eqs, n, DmlRel r))
228 (* TODO: deal with equalities involving just [DmlRel]s and [Prim]s. *) 259 (* TODO: deal with equalities involving just [DmlRel]s and [Prim]s. *)
229 | (_, eqso) => eqso 260 | (_, eqso) => eqso
230 val eqsOfClass : atomExp list -> Mono.exp' IM.map option = 261 val eqsOfClass : atomExp list -> atomExp IM.map option =
231 List.foldl accumulateEqs (SOME IM.empty) 262 List.foldl accumulateEqs (SOME IM.empty)
232 o chooseTwos 263 o chooseTwos
233 fun toAtomExps rel (cmp, e1, e2) = 264 fun toAtomExps rel (cmp, e1, e2) =
234 let 265 let
235 val qa = 266 val qa =
250 | Sql.Lt => Sql.Ge 281 | Sql.Lt => Sql.Ge
251 | Sql.Le => Sql.Gt 282 | Sql.Le => Sql.Gt
252 | Sql.Gt => Sql.Le 283 | Sql.Gt => Sql.Le
253 | Sql.Ge => Sql.Lt, 284 | Sql.Ge => Sql.Lt,
254 e1, e2) 285 e1, e2)
255 val markQuery = mapFormula (toAtomExps QueryArg) 286 val markQuery : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula ->
256 val markDml = mapFormula (toAtomExps DmlRel) 287 (Sql.cmp * atomExp option * atomExp option) formula =
257 val dnf = normalize negateCmp Dnf (Combo (Cnf, [markQuery fQuery, markDml fDml])) 288 mapFormula (toAtomExps QueryArg)
258 (* If one of the terms in a conjunction leads to a contradiction, which 289 val markDml : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula ->
259 is represented by [NONE], drop the entire conjunction. *) 290 (Sql.cmp * atomExp option * atomExp option) formula =
260 val sequenceOption = List.foldr (fn (SOME x, SOME xs) => SOME (x :: xs) | _ => NONE) 291 mapFormula (toAtomExps DmlRel)
261 (SOME []) 292 (* No eqs should have key conflicts because no variable is in two
262 in 293 equivalence classes, so the [#1] can be anything. *)
263 List.mapPartial (sequenceOption o map eqsOfClass o equivClasses) dnf 294 val mergeEqs : (atomExp IntBinaryMap.map option list
264 end 295 -> atomExp IntBinaryMap.map option) =
296 List.foldr (fn (SOME eqs, SOME acc) => SOME (IM.unionWith #1 (eqs, acc)) | _ => NONE)
297 (SOME IM.empty)
298 fun dnf (fQuery, fDml) =
299 normalize negateCmp Dnf (Combo (Cnf, [markQuery fQuery, markDml fDml]))
300 (* in *)
301 val conflictMaps : (Sql.cmp * Sql.sqexp * Sql.sqexp) formula
302 * (Sql.cmp * Sql.sqexp * Sql.sqexp) formula
303 -> atomExp IM.map list =
304 List.mapPartial (mergeEqs o map eqsOfClass o equivClasses) o dnf
305 (* end *)
265 306
266 val rec sqexpToFormula = 307 val rec sqexpToFormula =
267 fn Sql.SqTrue => Combo (Cnf, []) 308 fn Sql.SqTrue => Combo (Cnf, [])
268 | Sql.SqFalse => Combo (Dnf, []) 309 | Sql.SqFalse => Combo (Dnf, [])
269 | Sql.SqNot e => Negate (sqexpToFormula e) 310 | Sql.SqNot e => Negate (sqexpToFormula e)
271 | Sql.Binop (Sql.RLop l, p1, p2) => Combo (case l of Sql.And => Cnf | Sql.Or => Dnf, 312 | Sql.Binop (Sql.RLop l, p1, p2) => Combo (case l of Sql.And => Cnf | Sql.Or => Dnf,
272 [sqexpToFormula p1, sqexpToFormula p2]) 313 [sqexpToFormula p1, sqexpToFormula p2])
273 (* ASK: any other sqexps that can be props? *) 314 (* ASK: any other sqexps that can be props? *)
274 | _ => raise Match 315 | _ => raise Match
275 316
276 val rec queryToFormula = 317 fun renameTables tablePairs =
277 fn Sql.Query1 {From = tablePairs, Where = NONE, ...} => Combo (Cnf, [])
278 | Sql.Query1 {From = tablePairs, Where = SOME e, ...} =>
279 let 318 let
280 fun renameString table = 319 fun renameString table =
281 case List.find (fn (_, t) => table = t) tablePairs of 320 case List.find (fn (_, t) => table = t) tablePairs of
282 NONE => table 321 NONE => table
283 | SOME (realTable, _) => realTable 322 | SOME (realTable, _) => realTable
284 val renameSqexp = 323 val renameSqexp =
285 fn Sql.Field (table, field) => Sql.Field (renameString table, field) 324 fn Sql.Field (table, field) => Sql.Field (renameString table, field)
286 | e => e 325 | e => e
287 fun renameAtom (cmp, e1, e2) = (cmp, renameSqexp e1, renameSqexp e2) 326 fun renameAtom (cmp, e1, e2) = (cmp, renameSqexp e1, renameSqexp e2)
288 in 327 in
289 mapFormula renameAtom (sqexpToFormula e) 328 mapFormula renameAtom
290 end 329 end
330
331 val rec queryToFormula =
332 fn Sql.Query1 {Where = NONE, ...} => Combo (Cnf, [])
333 | Sql.Query1 {From = tablePairs, Where = SOME e, ...} =>
334 renameTables tablePairs (sqexpToFormula e)
291 | Sql.Union (q1, q2) => Combo (Dnf, [queryToFormula q1, queryToFormula q2]) 335 | Sql.Union (q1, q2) => Combo (Dnf, [queryToFormula q1, queryToFormula q2])
292 336
337 fun valsToFormula (table, vals) =
338 Combo (Cnf, map (fn (field, v) => Atom (Sql.Eq, Sql.Field (table, field), v)) vals)
339
293 val rec dmlToFormula = 340 val rec dmlToFormula =
294 fn Sql.Insert (table, vals) => 341 fn Sql.Insert tableVals => valsToFormula tableVals
295 Combo (Cnf, map (fn (field, v) => Atom (Sql.Eq, Sql.Field (table, field), v)) vals) 342 | Sql.Delete (table, wher) => renameTables [(table, "T")] (sqexpToFormula wher)
296 | Sql.Delete (_, wher) => sqexpToFormula wher
297 (* TODO: refine formula for the vals part, which could take into account the wher part. *) 343 (* 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)), 344 | Sql.Update (table, vals, wher) =>
299 dmlToFormula (Sql.Delete (table, wher))]) 345 let
346 val f = sqexpToFormula wher
347 fun update (positive, a) =
348 let
349 fun updateIfNecessary field =
350 case List.find (fn (f, _) => field = f) vals of
351 SOME (_, v) => (if positive then Sql.Eq else Sql.Ne,
352 Sql.Field (table, field),
353 v)
354 | NONE => a
355 in
356 case a of
357 (_, Sql.Field (_, field), _) => updateIfNecessary field
358 | (_, _, Sql.Field (_, field)) => updateIfNecessary field
359 | _ => a
360 end
361 in
362 renameTables [(table, "T")]
363 (Combo (Dnf, [f,
364 Combo (Cnf, [valsToFormula (table, vals),
365 mapFormulaSigned true update f])]))
366 end
300 367
301 val rec tablesQuery = 368 val rec tablesQuery =
302 fn Sql.Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs) 369 fn Sql.Query1 {From = tablePairs, ...} => SS.fromList (map #1 tablePairs)
303 | Sql.Union (q1, q2) => SS.union (tablesQuery q1, tablesQuery q2) 370 | Sql.Union (q1, q2) => SS.union (tablesQuery q1, tablesQuery q2)
304 371
414 481
415 fun fileMap doExp file = #1 (fileMapfold (fn x => fn _ => (doExp x, ())) file ()) 482 fun fileMap doExp file = #1 (fileMapfold (fn x => fn _ => (doExp x, ())) file ())
416 483
417 fun addChecking file = 484 fun addChecking file =
418 let 485 let
419 fun doExp queryInfo = 486 fun doExp (queryInfo as (tableToIndices, indexToQuery)) =
420 fn e' as ELet (v, t, 487 fn e' as ELet (v, t,
421 queryExp' as (EQuery {query = origQueryText, 488 queryExp' as (EQuery {query = origQueryText,
422 initial, body, state, tables, exps}, queryLoc), 489 initial, body, state, tables, exps}, queryLoc),
423 letBody) => 490 letBody) =>
424 let 491 let
458 state = state, 525 state = state,
459 tables = tables, 526 tables = tables,
460 exps = exps}, 527 exps = exps},
461 queryLoc) 528 queryLoc)
462 val (EQuery {query = queryText, ...}, _) = queryExp 529 val (EQuery {query = queryText, ...}, _) = queryExp
463 (* val () = Print.preface ("sqlcache> ", (MonoPrint.p_exp MonoEnv.empty queryText)); *) 530 val () = Print.preface ("sqlcache> ", (MonoPrint.p_exp MonoEnv.empty queryText));
464 val args = List.tabulate (numArgs, fn n => (ERel n, loc)) 531 val args = List.tabulate (numArgs, fn n => (ERel n, loc))
465 fun bind x f = Option.mapPartial f x 532 fun bind x f = Option.mapPartial f x
466 fun guard b x = if b then x else NONE 533 fun guard b x = if b then x else NONE
467 (* DEBUG: set first boolean argument to true to turn on printing. *) 534 (* DEBUG: set first boolean argument to true to turn on printing. *)
468 fun safe bound = not o effectful true (effectfulMap file) false bound 535 fun safe bound = not o effectful true (effectfulMap file) false bound
469 val attempt = 536 val attempt =
470 (* Ziv misses Haskell's do notation.... *) 537 (* Ziv misses Haskell's do notation.... *)
471 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) ( 538 guard (safe 0 queryText andalso safe 0 initial andalso safe 2 body) (
472 bind (Sql.parse Sql.query queryText) (fn queryParsed => 539 bind (Sql.parse Sql.query queryText) (fn queryParsed =>
473 bind (indexOfName v) (fn i => 540 bind (indexOfName v) (fn index =>
474 bind (IM.find (!urlifiedRel0s, i)) (fn urlifiedRel0 => 541 bind (IM.find (!urlifiedRel0s, index)) (fn urlifiedRel0 =>
475 SOME (wrapLets (ELet (v, t, 542 SOME (wrapLets (ELet (v, t,
476 cacheWrap (queryExp, i, urlifiedRel0, args), 543 cacheWrap (queryExp, index, urlifiedRel0, args),
477 incRelsBound 1 (length newVariables) letBody)), 544 incRelsBound 1 (length newVariables) letBody)),
478 SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, i)) 545 (SS.foldr (fn (tab, qi) => SIMM.insert (qi, tab, index))
479 queryInfo 546 tableToIndices
480 (tablesQuery queryParsed)))))) 547 (tablesQuery queryParsed),
548 IM.insert (indexToQuery, index, (queryParsed, numArgs))))))))
481 in 549 in
482 case attempt of 550 case attempt of
483 SOME pair => pair 551 SOME pair => pair
484 | NONE => (e', queryInfo) 552 | NONE => (e', queryInfo)
485 end 553 end
486 | ESeq ((EFfiApp ("Sqlcache", "dummy", _), _), (e', _)) => (e', queryInfo) 554 | ESeq ((EFfiApp ("Sqlcache", "dummy", _), _), (e', _)) => (e', queryInfo)
487 | e' => (e', queryInfo) 555 | e' => (e', queryInfo)
488 in 556 in
489 fileMapfold (fn exp => fn state => doExp state exp) file SIMM.empty 557 fileMapfold (fn exp => fn state => doExp state exp) file (SIMM.empty, IM.empty)
490 end 558 end
559
560 val gunk' : (((Sql.cmp * Sql.sqexp * Sql.sqexp) formula)
561 * ((Sql.cmp * Sql.sqexp * Sql.sqexp) formula)) list ref = ref []
491 562
492 fun invalidations (nQueryArgs, query, dml) = 563 fun invalidations (nQueryArgs, query, dml) =
493 let 564 let
494 val loc = ErrorMsg.dummySpan 565 val loc = ErrorMsg.dummySpan
495 val optionToExp = 566 val optionAtomExpToExp =
496 fn NONE => (ENone stringTyp, loc) 567 fn NONE => (ENone stringTyp, loc)
497 | SOME e => (ESome (stringTyp, (e, loc)), loc) 568 | SOME e => (ESome (stringTyp,
569 (case e of
570 DmlRel n => ERel n
571 | Prim p => EPrim p
572 (* TODO: make new type containing only these two. *)
573 | _ => raise Match,
574 loc)),
575 loc)
498 fun eqsToInvalidation eqs = 576 fun eqsToInvalidation eqs =
499 let 577 let
500 fun inv n = if n < 0 then [] else optionToExp (IM.find (eqs, n)) :: inv (n - 1) 578 fun inv n = if n < 0 then [] else IM.find (eqs, n) :: inv (n - 1)
501 in 579 in
502 inv (nQueryArgs - 1) 580 inv (nQueryArgs - 1)
503 end 581 end
504 in 582 (* *)
505 map (map eqsToInvalidation) (conflictMaps (queryToFormula query, dmlToFormula dml)) 583 val rec madeRedundantBy : atomExp option list * atomExp option list -> bool =
506 end 584 fn ([], []) => true
507 585 | (NONE :: xs, _ :: ys) => madeRedundantBy (xs, ys)
508 fun addFlushing (file, queryInfo) = 586 | (SOME x :: xs, SOME y :: ys) => equalAtomExp (x, y) andalso madeRedundantBy (xs, ys)
509 let 587 | _ => false
510 val allIndices : int list = SM.foldr (fn (x, acc) => IS.listItems x @ acc) [] queryInfo 588 fun removeRedundant' (xss, yss) =
511 fun flushes indices = map (fn i => ffiAppCache' ("flush", i, [])) indices 589 case xss of
590 [] => yss
591 | xs :: xss' =>
592 removeRedundant' (xss',
593 if List.exists (fn ys => madeRedundantBy (xs, ys)) (xss' @ yss)
594 then yss
595 else xs :: yss)
596 fun removeRedundant xss = removeRedundant' (xss, [])
597 val eqss = conflictMaps (queryToFormula query, dmlToFormula dml)
598 in
599 gunk' := (queryToFormula query, dmlToFormula dml) :: !gunk';
600 (map (map optionAtomExpToExp) o removeRedundant o map eqsToInvalidation) eqss
601 end
602
603 val gunk : Mono.exp list list list ref = ref []
604
605 fun addFlushing (file, queryInfo as (tableToIndices, indexToQuery)) =
606 let
607 val allIndices = SM.foldr (fn (x, acc) => IS.listItems x @ acc) [] tableToIndices
608 val flushes = map (fn i => ffiAppCache' ("flush", i, []))
512 val doExp = 609 val doExp =
513 fn dmlExp as EDml (dmlText, _) => 610 fn dmlExp as EDml (dmlText, _) =>
514 let 611 let
515 val indices = 612 val indices =
516 case Sql.parse Sql.dml dmlText of 613 case Sql.parse Sql.dml dmlText of
517 SOME dmlParsed => SIMM.findList (queryInfo, tableDml dmlParsed) 614 SOME dmlParsed =>
615 map (fn i => ((case IM.find (indexToQuery, i) of
616 NONE => ()
617 | SOME (queryParsed, numArgs) =>
618 gunk := invalidations (numArgs, queryParsed, dmlParsed) :: !gunk);
619 i)) (SIMM.findList (tableToIndices, tableDml dmlParsed))
518 | NONE => allIndices 620 | NONE => allIndices
519 in 621 in
520 sequence (flushes indices @ [dmlExp]) 622 sequence (flushes indices @ [dmlExp])
521 end 623 end
522 | e' => e' 624 | e' => e'