@@ -13,6 +13,19 @@ import qualified Data.Map as M
1313import qualified Data.Set as S
1414
1515-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
16+optimizeRelationalExpr :: DatabaseContext -> RelationalExpr -> Either RelationalError RelationalExpr
17+optimizeRelationalExpr context expr = runReader (optimizeRelationalExprReader expr) (mkRelationalExprState context)
18+
19+optimizeRelationalExprReader :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
20+optimizeRelationalExprReader expr = do
21+ eOptExpr <- applyStaticRelationalOptimization expr
22+ case eOptExpr of
23+ Left err -> pure (Left err)
24+ Right optExpr -> do
25+ applyStaticJoinElimination (applyStaticRestrictionPushdown (applyStaticRestrictionCollapse optExpr))
26+
27+optimizeDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContextExpr
28+optimizeDatabaseContextExpr context dbExpr = evalState (applyStaticDatabaseOptimization dbExpr) (freshDatabaseState context)
1629-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
1730
1831--should optimizations offer the possibility to pure errors? If they perform the up-front type-checking, maybe so
@@ -39,7 +52,7 @@ applyStaticRelationalOptimization (Project attrNameSet expr) = do
3952 optimizedSubExpression <- applyStaticRelationalOptimization expr
4053 case optimizedSubExpression of
4154 Left err -> pure $ Left err
42− Right optSubExpr -> applyStaticJoinElimination (Project attrNameSet optSubExpr)
55+ Right optSubExpr -> pure (Right (Project attrNameSet optSubExpr))
4356
4457applyStaticRelationalOptimization (Union exprA exprB) = do
4558 eOptExprA <- applyStaticRelationalOptimization exprA
@@ -129,14 +142,14 @@ applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
129142
130143applyStaticDatabaseOptimization (Assign name expr) = do
131144 context <- getStateContext
132− let optimizedExpr = runReader (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
145+ let optimizedExpr = optimizeRelationalExpr context expr
133146 case optimizedExpr of
134147 Left err -> pure $ Left err
135148 Right optimizedExpr2 -> pure $ Right (Assign name optimizedExpr2)
136149
137150applyStaticDatabaseOptimization (Insert targetName expr) = do
138151 context <- getStateContext
139− let optimizedExpr = runReader (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
152+ let optimizedExpr = optimizeRelationalExpr context expr
140153 case optimizedExpr of
141154 Left err -> pure $ Left err
142155 Right optimizedExpr2 -> if isEmptyRelationExpr optimizedExpr2 then -- if we are trying to insert an empty relation, do nothing
@@ -168,7 +181,7 @@ applyStaticDatabaseOptimization (RemoveInclusionDependency name) = pure $ Right
168181
169182applyStaticDatabaseOptimization (AddNotification name triggerExpr resultOldExpr resultNewExpr) = do
170183 context <- getStateContext
171− let eTriggerExprOpt = runReader (applyStaticRelationalOptimization triggerExpr) (RelationalExprStateElems context)
184+ let eTriggerExprOpt = optimizeRelationalExpr context triggerExpr
172185 case eTriggerExprOpt of
173186 Left err -> pure $ Left err
174187 Right triggerExprOpt -> --it doesn't make sense to optimize queries when we don't have their proper contexts
@@ -319,12 +332,13 @@ applyStaticJoinElimination expr@(Project attrNameSet (Join exprA exprB)) = do
319332 case eTypeB of
320333 Left err -> pure (Left err)
321334 Right typeB -> do
322− let matchesProjectionAttributes = if attrNames projType `S.isSubsetOf` attrNames typeA then
323− Just ((exprA, typeA), (exprB, typeB))
324− else if attrNames projType `S.isSubsetOf` attrNames typeB then
325− Just ((exprB, typeB), (exprA, typeA))
326− else
327− Nothing
335+ let matchesProjectionAttributes
336+ | attrNames projType `S.isSubsetOf` attrNames typeA =
337+ Just ((exprA, typeA), (exprB, typeB))
338+ | attrNames projType `S.isSubsetOf` attrNames typeB =
339+ Just ((exprB, typeB), (exprA, typeA))
340+ | otherwise =
341+ Nothing
328342 attrNames = A.attributeNameSet . attributes
329343 case matchesProjectionAttributes of
330344 Nothing -> -- this optimization does not apply
@@ -354,3 +368,85 @@ applyStaticJoinElimination expr@(Project attrNameSet (Join exprA exprB)) = do
354368
355369applyStaticJoinElimination expr = pure (Right expr)
356370
371+--restriction collapse converts chained restrictions into (Restrict (And pred1 pred2 pred3...))
372+ --this optimization should be fairly uncontroversial- performing a tuple scan once is cheaper than twice- parallelization can still take place
373+applyStaticRestrictionCollapse :: RelationalExpr -> RelationalExpr
374+applyStaticRestrictionCollapse expr =
375+ case expr of
376+ MakeRelationFromExprs _ _ -> expr
377+ MakeStaticRelation _ _ -> expr
378+ ExistingRelation _ -> expr
379+ RelationVariable _ _ -> expr
380+ Project attrs subexpr ->
381+ Project attrs (applyStaticRestrictionCollapse subexpr)
382+ Union sub1 sub2 ->
383+ Union (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
384+ Join sub1 sub2 ->
385+ Join (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
386+ Rename n1 n2 sub ->
387+ Rename n1 n2 (applyStaticRestrictionCollapse sub)
388+ Difference sub1 sub2 ->
389+ Difference (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
390+ Group n1 n2 sub ->
391+ Group n1 n2 (applyStaticRestrictionCollapse sub)
392+ Ungroup n1 sub ->
393+ Ungroup n1 (applyStaticRestrictionCollapse sub)
394+ Equals sub1 sub2 ->
395+ Equals (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
396+ NotEquals sub1 sub2 ->
397+ NotEquals (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
398+ Extend n sub ->
399+ Extend n (applyStaticRestrictionCollapse sub)
400+ Restrict firstPred _ ->
401+ let restrictions = sequentialRestrictions expr
402+ finalExpr = last restrictions
403+ optFinalExpr = case finalExpr of
404+ Restrict _ subexpr -> applyStaticRestrictionCollapse subexpr
405+ otherExpr -> otherExpr
406+ andPreds = foldr (\(Restrict subpred _) acc -> AndPredicate acc subpred) firstPred (tail restrictions) in
407+ Restrict andPreds optFinalExpr
408+
409+sequentialRestrictions :: RelationalExpr -> [RelationalExpr]
410+sequentialRestrictions expr@(Restrict _ subexpr) = expr:sequentialRestrictions subexpr
411+sequentialRestrictions _ = []
412+
413+--restriction pushdown only really makes sense for tuple-oriented storage schemes where performing a restriction before projection can cut down on the intermediate storage needed to store the data before the projection
414+-- x{proj} where c1 -> (x where c1){proj} #project on fewer tuples
415+-- (x union y) where c -> (x where c) union (y where c) #with a selective restriction, fewer tuples will need to be joined
416+applyStaticRestrictionPushdown :: RelationalExpr -> RelationalExpr
417+applyStaticRestrictionPushdown expr = case expr of
418+ MakeRelationFromExprs _ _ -> expr
419+ MakeStaticRelation _ _ -> expr
420+ ExistingRelation _ -> expr
421+ RelationVariable _ _ -> expr
422+ Project _ _ -> expr
423+ --this transformation cannot be inverted because the projection attributes might not exist in the inverted version
424+ Restrict restrictAttrs (Project projAttrs subexpr) ->
425+ Project projAttrs (Restrict restrictAttrs (applyStaticRestrictionPushdown subexpr))
426+ Restrict restrictAttrs (Union subexpr1 subexpr2) ->
427+ let optSub1 = applyStaticRestrictionPushdown subexpr1
428+ optSub2 = applyStaticRestrictionPushdown subexpr2 in
429+ Union (Restrict restrictAttrs optSub1) (Restrict restrictAttrs optSub2)
430+ Restrict attrs subexpr ->
431+ Restrict attrs (applyStaticRestrictionPushdown subexpr)
432+
433+ Union sub1 sub2 ->
434+ Union (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
435+ Join sub1 sub2 ->
436+ Join (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
437+ Rename n1 n2 sub ->
438+ Rename n1 n2 (applyStaticRestrictionPushdown sub)
439+ Difference sub1 sub2 ->
440+ Difference (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
441+ Group n1 n2 sub ->
442+ Group n1 n2 (applyStaticRestrictionPushdown sub)
443+ Ungroup n1 sub ->
444+ Ungroup n1 (applyStaticRestrictionPushdown sub)
445+ Equals sub1 sub2 ->
446+ Equals (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
447+ NotEquals sub1 sub2 ->
448+ NotEquals (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
449+ Extend n sub ->
450+ Extend n (applyStaticRestrictionPushdown sub)
451+
452+