project-m36-0.1: src/lib/ProjectM36/StaticOptimizer.hs
module ProjectM36.StaticOptimizer where
import ProjectM36.Base
import ProjectM36.RelationalExpression
import ProjectM36.Relation
import ProjectM36.Error
import qualified ProjectM36.AttributeNames as AS
import ProjectM36.TupleSet
import Control.Monad.State hiding (join)
import Data.Either (rights, lefts)
import Control.Monad.Trans.Reader
import qualified Data.Map as M
-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
--should optimizations offer the possibility to return errors? If they perform the up-front type-checking, maybe so
applyStaticRelationalOptimization :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
applyStaticRelationalOptimization e@(MakeStaticRelation _ _) = return $ Right e
applyStaticRelationalOptimization e@(MakeRelationFromExprs _ _) = return $ Right e
applyStaticRelationalOptimization e@(ExistingRelation _) = return $ Right e
applyStaticRelationalOptimization e@(RelationVariable _ _) = return $ Right e
--remove project of attributes which removes no attributes
applyStaticRelationalOptimization (Project attrNameSet expr) = do
relType <- typeForRelationalExpr expr
case relType of
Left err -> return $ Left err
Right relType2 -> if AS.all == attrNameSet then
applyStaticRelationalOptimization expr
else if AttributeNames (attributeNames relType2) == attrNameSet then
applyStaticRelationalOptimization expr
else do
optimizedSubExpression <- applyStaticRelationalOptimization expr
case optimizedSubExpression of
Left err -> return $ Left err
Right optSubExpr -> return $ Right $ Project attrNameSet optSubExpr
applyStaticRelationalOptimization (Union exprA exprB) = do
optExprA <- applyStaticRelationalOptimization exprA
optExprB <- applyStaticRelationalOptimization exprB
case optExprA of
Left err -> return $ Left err
Right optExprAx -> case optExprB of
Left err -> return $ Left err
Right optExprBx -> if optExprAx == optExprBx then
return (Right optExprAx)
else
return $ Right $ Union optExprAx optExprBx
applyStaticRelationalOptimization (Join exprA exprB) = do
optExprA <- applyStaticRelationalOptimization exprA
optExprB <- applyStaticRelationalOptimization exprB
case optExprA of
Left err -> return $ Left err
Right optExprA2 -> case optExprB of
Left err -> return $ Left err
Right optExprB2 -> if optExprA == optExprB then --A join A == A
return optExprA
else
return $ Right (Join optExprA2 optExprB2)
applyStaticRelationalOptimization (Difference exprA exprB) = do
optExprA <- applyStaticRelationalOptimization exprA
optExprB <- applyStaticRelationalOptimization exprB
case optExprA of
Left err -> return $ Left err
Right optExprA2 -> case optExprB of
Left err -> return $ Left err
Right optExprB2 -> if optExprA == optExprB then do --A difference A == A where false
eEmptyRel <- typeForRelationalExpr optExprA2
case eEmptyRel of
Left err -> pure (Left err)
Right emptyRel -> pure (Right (ExistingRelation emptyRel))
else
return $ Right (Difference optExprA2 optExprB2)
applyStaticRelationalOptimization e@(Rename _ _ _) = return $ Right e
applyStaticRelationalOptimization (Group oldAttrNames newAttrName expr) = do
return $ Right $ Group oldAttrNames newAttrName expr
applyStaticRelationalOptimization (Ungroup attrName expr) = do
return $ Right $ Ungroup attrName expr
--remove restriction of nothing
applyStaticRelationalOptimization (Restrict predicate expr) = do
optimizedPredicate <- applyStaticPredicateOptimization predicate
case optimizedPredicate of
Left err -> return $ Left err
Right optimizedPredicate2 -> if optimizedPredicate2 == TruePredicate then
applyStaticRelationalOptimization expr
else if optimizedPredicate2 == NotPredicate TruePredicate then do
attributesRel <- typeForRelationalExpr expr
case attributesRel of
Left err -> return $ Left err
Right attributesRelA -> return $ Right $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
else do
optimizedSubExpression <- applyStaticRelationalOptimization expr
case optimizedSubExpression of
Left err -> return $ Left err
Right optSubExpr -> return $ Right $ Restrict optimizedPredicate2 optSubExpr
applyStaticRelationalOptimization e@(Equals _ _) = return $ Right e
applyStaticRelationalOptimization e@(NotEquals _ _) = return $ Right e
applyStaticRelationalOptimization e@(Extend _ _) = return $ 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 = runReader (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
case optimizedExpr of
Left err -> return $ Left err
Right optimizedExpr2 -> return $ Right (Assign name optimizedExpr2)
applyStaticDatabaseOptimization (Insert name expr) = do
context <- getStateContext
let optimizedExpr = runReader (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
case optimizedExpr of
Left err -> return $ Left err
Right optimizedExpr2 -> return $ Right (Insert name optimizedExpr2)
applyStaticDatabaseOptimization (Delete name predicate) = do
context <- getStateContext
let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
case optimizedPredicate of
Left err -> return $ Left err
Right optimizedPredicate2 -> return $ Right (Delete name optimizedPredicate2)
applyStaticDatabaseOptimization (Update name upmap predicate) = do
context <- getStateContext
let optimizedPredicate = runReader (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
case optimizedPredicate of
Left err -> return $ Left err
Right optimizedPredicate2 -> return $ Right (Update name upmap optimizedPredicate2)
applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = return $ Right dep
applyStaticDatabaseOptimization (RemoveInclusionDependency name) = return $ Right (RemoveInclusionDependency name)
applyStaticDatabaseOptimization (AddNotification name triggerExpr resultExpr) = do
context <- getStateContext
let eTriggerExprOpt = runReader (applyStaticRelationalOptimization triggerExpr) (RelationalExprStateElems context)
case eTriggerExprOpt of
Left err -> pure $ Left err
Right triggerExprOpt -> do
let eResultExprOpt = runReader (applyStaticRelationalOptimization resultExpr) (RelationalExprStateElems context)
case eResultExprOpt of
Left err -> pure $ Left err
Right resultExprOpt -> pure (Right (AddNotification name triggerExprOpt resultExprOpt))
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)
--optimization: from pgsql lists- check for join condition referencing foreign key- if join projection project away the referenced table, then it does not need to be scanned
--applyStaticDatabaseOptimization (MultipleExpr exprs) = return $ Right $ MultipleExpr exprs
--for multiple expressions, we must evaluate
applyStaticDatabaseOptimization (MultipleExpr exprs) = do
context <- getStateContext
let optExprs = evalState substateRunner ((contextWithEmptyTupleSets context), M.empty, False)
let errors = lefts optExprs
if length errors > 0 then
return $ Left (head errors)
else
return $ Right $ MultipleExpr (rights optExprs)
where
substateRunner = forM exprs $ \expr -> do
--a previous expression could create a relvar, we don't want to miss it, so we clear the tuples and execute the expression to get an empty relation in the relvar
_ <- evalDatabaseContextExpr expr
applyStaticDatabaseOptimization expr
--this error handling could be improved with some lifting presumably
--restore original context
applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
applyStaticPredicateOptimization predicate = return $ Right predicate