module ProjectM36.StaticOptimizer where
import ProjectM36.Base
import ProjectM36.RelationalExpression
import ProjectM36.Relation
import ProjectM36.Error
import qualified ProjectM36.Attribute as A
import qualified ProjectM36.AttributeNames as AS
import ProjectM36.TupleSet
import Control.Monad.State
import Data.Either (rights, lefts)
import Control.Monad.Trans.Reader
import qualified Data.Map as M
import qualified Data.Set as S
optimizeRelationalExpr :: DatabaseContext -> RelationalExpr -> Either RelationalError RelationalExpr
optimizeRelationalExpr context expr = runReader (optimizeRelationalExprReader expr) (mkRelationalExprState context)
optimizeRelationalExprReader :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
optimizeRelationalExprReader expr = do
eOptExpr <- applyStaticRelationalOptimization expr
case eOptExpr of
Left err -> pure (Left err)
Right optExpr ->
applyStaticJoinElimination (applyStaticRestrictionPushdown (applyStaticRestrictionCollapse optExpr))
optimizeDatabaseContextExpr :: DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContextExpr
optimizeDatabaseContextExpr context dbExpr = evalState (applyStaticDatabaseOptimization dbExpr) (freshDatabaseState context)
applyStaticRelationalOptimization :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
applyStaticRelationalOptimization e@(MakeStaticRelation _ _) = pure $ Right e
applyStaticRelationalOptimization e@(MakeRelationFromExprs _ _) = pure $ Right e
applyStaticRelationalOptimization e@(ExistingRelation _) = pure $ Right e
applyStaticRelationalOptimization e@(RelationVariable _ _) = pure $ Right e
applyStaticRelationalOptimization (Project attrNameSet expr) = do
relType <- typeForRelationalExpr expr
case relType of
Left err -> pure $ Left err
Right relType2
| AS.all == attrNameSet ->
applyStaticRelationalOptimization expr
| AttributeNames (attributeNames relType2) == attrNameSet ->
applyStaticRelationalOptimization expr
| otherwise -> do
optimizedSubExpression <- applyStaticRelationalOptimization expr
case optimizedSubExpression of
Left err -> pure $ Left err
Right optSubExpr -> pure (Right (Project attrNameSet optSubExpr))
applyStaticRelationalOptimization (Union exprA exprB) = do
eOptExprA <- applyStaticRelationalOptimization exprA
eOptExprB <- applyStaticRelationalOptimization exprB
case eOptExprA of
Left err -> pure $ Left err
Right optExprA -> case eOptExprB of
Left err -> pure $ Left err
Right optExprB ->
case (optExprA, optExprB) of
(Restrict predA (RelationVariable nameA ()),
Restrict predB (RelationVariable nameB ())) | nameA == nameB -> pure (Right (Restrict (AndPredicate predA predB) (RelationVariable nameA ())))
_ -> if optExprA == optExprB then
pure (Right optExprA)
else
pure $ Right $ Union optExprA optExprB
applyStaticRelationalOptimization (Join exprA exprB) = do
eOptExprA <- applyStaticRelationalOptimization exprA
eOptExprB <- applyStaticRelationalOptimization exprB
case eOptExprA of
Left err -> pure $ Left err
Right optExprA -> case eOptExprB of
Left err -> pure $ Left err
Right optExprB ->
case (optExprA, optExprB) of
(Restrict predA (RelationVariable nameA ()),
Restrict predB (RelationVariable nameB ())) | nameA == nameB -> pure (Right (Restrict (AndPredicate predA predB) (RelationVariable nameA ())))
_ -> if optExprA == optExprB then
pure (Right optExprA)
else
pure (Right (Join optExprA optExprB))
applyStaticRelationalOptimization (Difference exprA exprB) = do
optExprA <- applyStaticRelationalOptimization exprA
optExprB <- applyStaticRelationalOptimization exprB
case optExprA of
Left err -> pure $ Left err
Right optExprA2 -> case optExprB of
Left err -> pure $ Left err
Right optExprB2 -> if optExprA == optExprB then do
eEmptyRel <- typeForRelationalExpr optExprA2
case eEmptyRel of
Left err -> pure (Left err)
Right emptyRel -> pure (Right (ExistingRelation emptyRel))
else
pure $ Right (Difference optExprA2 optExprB2)
applyStaticRelationalOptimization e@Rename{} = pure $ Right e
applyStaticRelationalOptimization (Group oldAttrNames newAttrName expr) =
pure $ Right $ Group oldAttrNames newAttrName expr
applyStaticRelationalOptimization (Ungroup attrName expr) =
pure $ Right $ Ungroup attrName expr
applyStaticRelationalOptimization (Restrict predicate expr) = do
optimizedPredicate <- applyStaticPredicateOptimization predicate
case optimizedPredicate of
Left err -> pure $ Left err
Right optimizedPredicate2
| isTrueExpr optimizedPredicate2 -> applyStaticRelationalOptimization expr
| isFalseExpr optimizedPredicate2 -> do
attributesRel <- typeForRelationalExpr expr
case attributesRel of
Left err -> pure $ Left err
Right attributesRelA -> pure $ Right $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
| otherwise -> do
optimizedSubExpression <- applyStaticRelationalOptimization expr
case optimizedSubExpression of
Left err -> pure $ Left err
Right optSubExpr -> pure $ Right $ Restrict optimizedPredicate2 optSubExpr
applyStaticRelationalOptimization e@(Equals _ _) = pure $ Right e
applyStaticRelationalOptimization e@(NotEquals _ _) = pure $ Right e
applyStaticRelationalOptimization e@(Extend _ _) = pure $ Right e
applyStaticRelationalOptimization e@(With _ _) = pure $ Right e
applyStaticDatabaseOptimization :: DatabaseContextExpr -> DatabaseState (Either RelationalError DatabaseContextExpr)
applyStaticDatabaseOptimization x@NoOperation = pure $ Right x
applyStaticDatabaseOptimization x@(Define _ _) = pure $ Right x
applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
applyStaticDatabaseOptimization (Assign name expr) = do
context <- getStateContext
let optimizedExpr = optimizeRelationalExpr context expr
case optimizedExpr of
Left err -> pure $ Left err
Right optimizedExpr2 -> pure $ Right (Assign name optimizedExpr2)
applyStaticDatabaseOptimization (Insert targetName expr) = do
context <- getStateContext
let optimizedExpr = optimizeRelationalExpr context expr
case optimizedExpr of
Left err -> pure $ Left err
Right optimizedExpr2 -> if isEmptyRelationExpr optimizedExpr2 then
pure (Right NoOperation)
else
case optimizedExpr2 of
RelationVariable insName () | insName == targetName -> pure (Right NoOperation)
_ -> pure $ Right (Insert targetName optimizedExpr2)
applyStaticDatabaseOptimization (Delete name predicate) = do
context <- getStateContext
let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
case optimizedPredicate of
Left err -> pure $ Left err
Right optimizedPredicate2 -> pure $ Right (Delete name optimizedPredicate2)
applyStaticDatabaseOptimization (Update name upmap predicate) = do
context <- getStateContext
let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
case optimizedPredicate of
Left err -> pure $ Left err
Right optimizedPredicate2 -> pure $ Right (Update name upmap optimizedPredicate2)
applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = pure $ Right dep
applyStaticDatabaseOptimization (RemoveInclusionDependency name) = pure $ Right (RemoveInclusionDependency name)
applyStaticDatabaseOptimization (AddNotification name triggerExpr resultOldExpr resultNewExpr) = do
context <- getStateContext
let eTriggerExprOpt = optimizeRelationalExpr context triggerExpr
case eTriggerExprOpt of
Left err -> pure $ Left err
Right triggerExprOpt ->
pure (Right (AddNotification name triggerExprOpt resultOldExpr resultNewExpr))
applyStaticDatabaseOptimization notif@(RemoveNotification _) = pure (Right notif)
applyStaticDatabaseOptimization c@(AddTypeConstructor _ _) = pure (Right c)
applyStaticDatabaseOptimization c@(RemoveTypeConstructor _) = pure (Right c)
applyStaticDatabaseOptimization c@(RemoveAtomFunction _) = pure (Right c)
applyStaticDatabaseOptimization c@(RemoveDatabaseContextFunction _) = pure (Right c)
applyStaticDatabaseOptimization c@(ExecuteDatabaseContextFunction _ _) = pure (Right c)
applyStaticDatabaseOptimization (MultipleExpr exprs) = do
context <- getStateContext
let optExprs = evalState substateRunner (contextWithEmptyTupleSets context, M.empty, False)
let errors = lefts optExprs
if not (null errors) then
pure $ Left (head errors)
else
pure $ Right $ MultipleExpr (rights optExprs)
where
substateRunner = forM exprs $ \expr -> do
_ <- evalDatabaseContextExpr expr
applyStaticDatabaseOptimization expr
applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
applyStaticPredicateOptimization predi = do
eOptPred <- case predi of
AndPredicate pred1 pred2 -> do
eOptPred1 <- applyStaticPredicateOptimization pred1
case eOptPred1 of
Left err -> pure (Left err)
Right optPred1 -> do
eOptPred2 <- applyStaticPredicateOptimization pred2
case eOptPred2 of
Left err -> pure (Left err)
Right optPred2 ->
if optPred1 == optPred2 then
pure (Right optPred1)
else
pure (Right (AndPredicate optPred1 optPred2))
OrPredicate pred1 pred2 -> do
eOptPred1 <- applyStaticPredicateOptimization pred1
case eOptPred1 of
Left err -> pure (Left err)
Right optPred1 -> do
eOptPred2 <- applyStaticPredicateOptimization pred2
case eOptPred2 of
Left err -> pure (Left err)
Right optPred2 | optPred1 == optPred2 -> pure (Right optPred1)
| isTrueExpr optPred1 -> pure (Right optPred1)
| isTrueExpr optPred2 -> pure (Right optPred2)
| otherwise -> pure (Right (OrPredicate optPred1 optPred2))
AttributeEqualityPredicate attrNameA (AttributeAtomExpr attrNameB) ->
if attrNameA == attrNameB then
pure (Right TruePredicate)
else
pure (Right predi)
AttributeEqualityPredicate{} -> pure (Right predi)
TruePredicate -> pure $ Right predi
NotPredicate{} -> pure $ Right predi
RelationalExprPredicate{} -> pure (Right predi)
AtomExprPredicate{} -> pure (Right predi)
case eOptPred of
Left err -> pure (Left err)
Right optPred ->
let attrMap = findStaticRestrictionPredicates optPred in
pure (Right (replaceStaticAtomExprs optPred attrMap))
isTrueExpr :: RestrictionPredicateExpr -> Bool
isTrueExpr TruePredicate = True
isTrueExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom True))) = True
isTrueExpr _ = False
isFalseExpr :: RestrictionPredicateExpr -> Bool
isFalseExpr (NotPredicate expr) = isTrueExpr expr
isFalseExpr (AtomExprPredicate (NakedAtomExpr (BoolAtom False))) = True
isFalseExpr _ = False
isEmptyRelationExpr :: RelationalExpr -> Bool
isEmptyRelationExpr (MakeRelationFromExprs _ []) = True
isEmptyRelationExpr (MakeStaticRelation _ tupSet) = null (asList tupSet)
isEmptyRelationExpr (ExistingRelation rel) = rel == emptyRelationWithAttrs (attributes rel)
isEmptyRelationExpr _ = False
replaceStaticAtomExprs :: RestrictionPredicateExpr -> M.Map AttributeName AtomExpr -> RestrictionPredicateExpr
replaceStaticAtomExprs predIn replaceMap = case predIn of
AttributeEqualityPredicate newAttrName (AttributeAtomExpr matchName) -> case M.lookup matchName replaceMap of
Nothing -> predIn
Just newVal -> AttributeEqualityPredicate newAttrName newVal
AttributeEqualityPredicate{} -> predIn
AndPredicate pred1 pred2 -> AndPredicate (replaceStaticAtomExprs pred1 replaceMap) (replaceStaticAtomExprs pred2 replaceMap)
OrPredicate pred1 pred2 -> OrPredicate (replaceStaticAtomExprs pred1 replaceMap) (replaceStaticAtomExprs pred2 replaceMap)
NotPredicate pred1 -> NotPredicate (replaceStaticAtomExprs pred1 replaceMap)
TruePredicate -> predIn
RelationalExprPredicate{} -> predIn
AtomExprPredicate{} -> predIn
findStaticRestrictionPredicates :: RestrictionPredicateExpr -> M.Map AttributeName AtomExpr
findStaticRestrictionPredicates (AttributeEqualityPredicate attrName atomExpr) =
case atomExpr of
val@NakedAtomExpr{} -> M.singleton attrName val
val@ConstructedAtomExpr{} -> M.singleton attrName val
_ -> M.empty
findStaticRestrictionPredicates (AndPredicate pred1 pred2) =
M.union (findStaticRestrictionPredicates pred1) (findStaticRestrictionPredicates pred2)
findStaticRestrictionPredicates (OrPredicate pred1 pred2) =
M.union (findStaticRestrictionPredicates pred1) (findStaticRestrictionPredicates pred2)
findStaticRestrictionPredicates (NotPredicate predi) = findStaticRestrictionPredicates predi
findStaticRestrictionPredicates TruePredicate = M.empty
findStaticRestrictionPredicates RelationalExprPredicate{} = M.empty
findStaticRestrictionPredicates AtomExprPredicate{} = M.empty
isStaticAtomExpr :: AtomExpr -> Bool
isStaticAtomExpr NakedAtomExpr{} = True
isStaticAtomExpr ConstructedAtomExpr{} = True
isStaticAtomExpr AttributeAtomExpr{} = False
isStaticAtomExpr FunctionAtomExpr{} = False
isStaticAtomExpr RelationAtomExpr{} = False
applyStaticJoinElimination :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
applyStaticJoinElimination expr@(Project attrNameSet (Join exprA exprB)) = do
relState <- ask
eProjType <- typeForRelationalExpr expr
eTypeA <- typeForRelationalExpr exprA
eTypeB <- typeForRelationalExpr exprB
case eProjType of
Left err -> pure (Left err)
Right projType ->
case eTypeA of
Left err -> pure (Left err)
Right typeA ->
case eTypeB of
Left err -> pure (Left err)
Right typeB -> do
let matchesProjectionAttributes
| attrNames projType `S.isSubsetOf` attrNames typeA =
Just ((exprA, typeA), (exprB, typeB))
| attrNames projType `S.isSubsetOf` attrNames typeB =
Just ((exprB, typeB), (exprA, typeA))
| otherwise =
Nothing
attrNames = A.attributeNameSet . attributes
case matchesProjectionAttributes of
Nothing ->
pure (Right expr)
Just ((joinedExpr, joinedType), (unjoinedExpr, _)) -> do
incDeps <- inclusionDependencies . stateElemsContext <$> ask
let fkConstraint = foldM isFkConstraint False incDeps
isFkConstraint acc (InclusionDependency (Project subattrNames subrv) (Project _ superrv)) =
case runReader (evalAttributeNames subattrNames expr) relState of
Left _ -> pure acc
Right subAttrNameSet ->
pure (acc || (joinedExpr == subrv &&
unjoinedExpr == superrv &&
A.attributeNamesContained subAttrNameSet (A.attributeNameSet (attributes joinedType))
))
isFkConstraint acc _ = pure acc
case fkConstraint of
Right True ->
applyStaticRelationalOptimization (Project attrNameSet joinedExpr)
Right False ->
pure (Right expr)
Left err ->
pure (Left err)
applyStaticJoinElimination expr = pure (Right expr)
applyStaticRestrictionCollapse :: RelationalExpr -> RelationalExpr
applyStaticRestrictionCollapse expr =
case expr of
MakeRelationFromExprs _ _ -> expr
MakeStaticRelation _ _ -> expr
ExistingRelation _ -> expr
RelationVariable _ _ -> expr
With _ _ -> expr
Project attrs subexpr ->
Project attrs (applyStaticRestrictionCollapse subexpr)
Union sub1 sub2 ->
Union (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
Join sub1 sub2 ->
Join (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
Rename n1 n2 sub ->
Rename n1 n2 (applyStaticRestrictionCollapse sub)
Difference sub1 sub2 ->
Difference (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
Group n1 n2 sub ->
Group n1 n2 (applyStaticRestrictionCollapse sub)
Ungroup n1 sub ->
Ungroup n1 (applyStaticRestrictionCollapse sub)
Equals sub1 sub2 ->
Equals (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
NotEquals sub1 sub2 ->
NotEquals (applyStaticRestrictionCollapse sub1) (applyStaticRestrictionCollapse sub2)
Extend n sub ->
Extend n (applyStaticRestrictionCollapse sub)
Restrict firstPred _ ->
let restrictions = sequentialRestrictions expr
finalExpr = last restrictions
optFinalExpr = case finalExpr of
Restrict _ subexpr -> applyStaticRestrictionCollapse subexpr
otherExpr -> otherExpr
andPreds = foldr (\(Restrict subpred _) acc -> AndPredicate acc subpred) firstPred (tail restrictions) in
Restrict andPreds optFinalExpr
sequentialRestrictions :: RelationalExpr -> [RelationalExpr]
sequentialRestrictions expr@(Restrict _ subexpr) = expr:sequentialRestrictions subexpr
sequentialRestrictions _ = []
applyStaticRestrictionPushdown :: RelationalExpr -> RelationalExpr
applyStaticRestrictionPushdown expr = case expr of
MakeRelationFromExprs _ _ -> expr
MakeStaticRelation _ _ -> expr
ExistingRelation _ -> expr
RelationVariable _ _ -> expr
With _ _ -> expr
Project _ _ -> expr
Restrict restrictAttrs (Project projAttrs subexpr) ->
Project projAttrs (Restrict restrictAttrs (applyStaticRestrictionPushdown subexpr))
Restrict restrictAttrs (Union subexpr1 subexpr2) ->
let optSub1 = applyStaticRestrictionPushdown subexpr1
optSub2 = applyStaticRestrictionPushdown subexpr2 in
Union (Restrict restrictAttrs optSub1) (Restrict restrictAttrs optSub2)
Restrict attrs subexpr ->
Restrict attrs (applyStaticRestrictionPushdown subexpr)
Union sub1 sub2 ->
Union (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
Join sub1 sub2 ->
Join (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
Rename n1 n2 sub ->
Rename n1 n2 (applyStaticRestrictionPushdown sub)
Difference sub1 sub2 ->
Difference (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
Group n1 n2 sub ->
Group n1 n2 (applyStaticRestrictionPushdown sub)
Ungroup n1 sub ->
Ungroup n1 (applyStaticRestrictionPushdown sub)
Equals sub1 sub2 ->
Equals (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
NotEquals sub1 sub2 ->
NotEquals (applyStaticRestrictionPushdown sub1) (applyStaticRestrictionPushdown sub2)
Extend n sub ->
Extend n (applyStaticRestrictionPushdown sub)