on master help sign in
WIP compilation checkpoint after refactoring RelationalExpression and DatabaseExpression apart and partitioning StaticOptimizers

- also create DatabaseContextEvalState which includes the constraint validator so that the validator function can include the static optimizer

commit47073bca91b10e01d8ebf076fec6f61cdf400a85 authorAgentM <agentm@themactionfaction.com> dateThu 2 Mar 2017, 22:36 (10 years ago) parent8ea9b8c
17 files changed+722−629
project-m36.cabal+6−1 src/bin/TutorialD/Interpreter/DatabaseContextExpr.hs+17−10 src/lib/ProjectM36/Base.hs+1 src/lib/ProjectM36/Client.hs+31−20 src/lib/ProjectM36/DatabaseContext.hs+8 src/lib/ProjectM36/DatabaseContextExpression.hs+281 src/lib/ProjectM36/InclusionDependency.hs−1 src/lib/ProjectM36/InclusionDependencyValidation.hs+30 src/lib/ProjectM36/IsomorphicSchema.hs+20−15 src/lib/ProjectM36/Notifications.hs+1 src/lib/ProjectM36/RelationalExpression.hs+3−314 src/lib/ProjectM36/RelationalExpressionState.hs+42 src/lib/ProjectM36/StaticOptimizer.hs−268 src/lib/ProjectM36/StaticOptimizer/DatabaseContextExpression.hs+86 src/lib/ProjectM36/StaticOptimizer/InclusionDependencies.hs+84 src/lib/ProjectM36/StaticOptimizer/RelationalExpression.hs+111 src/lib/ProjectM36/TransGraphRelationalExpression.hs+1
project-m36.cabal+6−1
@@ -88,13 +88,18 @@ Library
8888 ProjectM36.AtomFunctionBody,
8989 ProjectM36.RelationalExpression,
9090 ProjectM36.Relation.Show.HTML,
91− ProjectM36.StaticOptimizer,
91+ ProjectM36.StaticOptimizer.RelationalExpression,
92+ ProjectM36.StaticOptimizer.InclusionDependencies,
93+ ProjectM36.StaticOptimizer.DatabaseContextExpression,
94+ ProjectM36.InclusionDependencyValidation,
9295 ProjectM36.Relation.Show.Term,
9396 ProjectM36.Relation.Show.CSV,
9497 ProjectM36.Relation.Show.Gnuplot,
9598 ProjectM36.Relation.Parse.CSV,
9699 ProjectM36.IsomorphicSchema,
97100 ProjectM36.InclusionDependency,
101+ ProjectM36.DatabaseContextExpression,
102+ ProjectM36.RelationalExpressionState,
98103 ProjectM36.Client,
99104 ProjectM36.Persist,
100105 ProjectM36.AtomType,
src/bin/TutorialD/Interpreter/DatabaseContextExpr.hs+17−10
@@ -8,12 +8,13 @@ import TutorialD.Interpreter.RelationalExpr
88import TutorialD.Interpreter.Types
99import qualified Data.Map as M
1010import Control.Monad.State
11−import ProjectM36.StaticOptimizer
11+import ProjectM36.StaticOptimizer.DatabaseContextExpression
12+import ProjectM36.DatabaseContextExpression
1213import qualified ProjectM36.Error as PM36E
1314import ProjectM36.Error
14−import ProjectM36.RelationalExpression
1515import ProjectM36.Key
1616import ProjectM36.FunctionalDependency
17+import ProjectM36.InclusionDependencyValidation
1718import Data.Monoid
1819
1920--parsers which create "database expressions" which modify the database context (such as relvar assignment)
@@ -181,18 +182,24 @@ removeAtomFunctionP = do
181182databaseExprOpP :: Parser DatabaseContextExpr
182183databaseExprOpP = multipleDatabaseContextExprP
183184
184−evalDatabaseContextExpr :: Bool -> DatabaseContext -> DatabaseContextExpr -> Either RelationalError DatabaseContext
185−evalDatabaseContextExpr useOptimizer context expr = do
186− optimizedExpr <- evalState (applyStaticDatabaseOptimization expr) context
187− case runState (evalContextExpr (if useOptimizer then optimizedExpr else expr)) context of
188− (Nothing, context') -> Right context'
189− (Just err, _) -> Left err
190−
185+evalDatabaseContextExpr :: Bool -> DatabaseContextExpr -> DatabaseState (Maybe RelationalError)
186+evalDatabaseContextExpr useOptimizer expr = do
187+ eOptimizedExpr <- applyStaticDatabaseOptimization expr
188+ case eOptimizedExpr of
189+ Left err -> pure (Just err)
190+ Right optimizedExpr -> evalContextExpr (if useOptimizer then optimizedExpr else expr)
191191
192192interpretDatabaseContextExpr :: DatabaseContext -> T.Text -> Either RelationalError DatabaseContext
193193interpretDatabaseContextExpr context tutdstring = case parse databaseExprOpP "" tutdstring of
194194 Left err -> Left $ PM36E.ParseError (T.pack (show err))
195− Right parsed -> evalDatabaseContextExpr True context parsed
195+ Right parsed -> case runState (evalDatabaseContextExpr True parsed) dbContextState of
196+ (Just err, _) -> Left err
197+ (Nothing, evaldState) -> Right (dbcontext evaldState)
198+ where
199+ dbContextState = DatabaseContextEvalState { dbcontext = context,
200+ constraintValidator = checkConstraints
201+ }
202+
196203
197204{-
198205--no optimization
src/lib/ProjectM36/Base.hs+1
@@ -489,3 +489,4 @@ data MergeStrategy =
489489 deriving (Eq, Show, Binary, Generic, NFData)
490490
491491
492+
src/lib/ProjectM36/Client.hs+31−20
@@ -78,11 +78,15 @@ module ProjectM36.Client
7878import ProjectM36.Base hiding (inclusionDependencies) --defined in this module as well
7979import qualified ProjectM36.Base as B
8080import ProjectM36.Error
81−import ProjectM36.StaticOptimizer
81+import ProjectM36.StaticOptimizer.DatabaseContextExpression
8282import ProjectM36.Key
8383import qualified ProjectM36.IsomorphicSchema as Schema
8484import Control.Monad.State
8585import qualified ProjectM36.RelationalExpression as RE
86+import qualified ProjectM36.RelationalExpressionState as RES
87+import ProjectM36.InclusionDependencyValidation
88+import qualified ProjectM36.DatabaseContextExpression as DCE
89+import ProjectM36.DatabaseContextExpression
8690import ProjectM36.DatabaseContext (basicDatabaseContext)
8791import ProjectM36.TransactionGraph
8892import qualified ProjectM36.Transaction as Trans
@@ -504,7 +508,7 @@ executeRelationalExpr sessionId (InProcessConnection conf) expr = excEither $ at
504508 Right expr
505509 case expr' of
506510 Left err -> pure (Left err)
507− Right expr'' -> case evalState (RE.evalRelationalExpr expr'') (RE.mkRelationalExprState (Sess.concreteDatabaseContext session)) of
511+ Right expr'' -> case evalState (RE.evalRelationalExpr expr'') (RES.mkRelationalExprState (Sess.concreteDatabaseContext session)) of
508512 Left err -> pure (Left err)
509513 Right rel -> pure (force (Right rel)) -- this is necessary so that any undefined/error exceptions are spit out here
510514executeRelationalExpr sessionId conn@(RemoteProcessConnection _) relExpr = remoteCall conn (ExecuteRelationalExpr sessionId relExpr)
@@ -523,12 +527,12 @@ executeDatabaseContextExpr sessionId (InProcessConnection conf) expr = excMaybe
523527 Schema.processDatabaseContextExprInSchema schema expr
524528 case expr' of
525529 Left err -> pure (Just err)
526− Right expr'' -> case runState (RE.evalContextExpr expr'') (Sess.concreteDatabaseContext session) of
530+ Right expr'' -> case runState (DCE.evalContextExpr expr'') (defaultDatabaseContextEvalState (Sess.concreteDatabaseContext session)) of
527531 (Just err,_) -> return $ Just err
528− (Nothing, context') -> do
532+ (Nothing, evaldState) -> do
529533 let newDiscon = DisconnectedTransaction (Sess.parentId session) newSchemas
530534 newSubschemas = Schema.processDatabaseContextExprSchemasUpdate (Sess.subschemas session) expr
531− newSchemas = Schemas context' newSubschemas
535+ newSchemas = Schemas (dbcontext evaldState) newSubschemas
532536 newSession = Session newDiscon (Sess.schemaName session)
533537 STMMap.insert newSession sessionId sessions
534538 pure Nothing
@@ -545,7 +549,7 @@ executeDatabaseContextIOExpr sessionId (InProcessConnection conf) expr = excMayb
545549 case eSession of
546550 Left err -> pure $ Just err
547551 Right session -> do
548− res <- RE.evalDatabaseContextIOExpr scriptSession (Sess.concreteDatabaseContext session) expr
552+ res <- DCE.evalDatabaseContextIOExpr scriptSession (Sess.concreteDatabaseContext session) expr
549553 case res of
550554 Left err -> pure (Just err)
551555 Right context' -> do
@@ -576,7 +580,7 @@ executeCommitExprSTM_ oldContext newContext nodes = do
576580 let nots = notifications oldContext
577581 fireNots = notificationChanges nots oldContext newContext
578582 evaldNots = M.map mkEvaldNot fireNots
579− mkEvaldNot notif = EvaluatedNotification { notification = notif, reportRelation = evalState (RE.evalRelationalExpr (reportExpr notif)) (RE.mkRelationalExprState oldContext) }
583+ mkEvaldNot notif = EvaluatedNotification { notification = notif, reportRelation = evalState (RE.evalRelationalExpr (reportExpr notif)) (RES.mkRelationalExprState oldContext) }
580584 pure (evaldNots, nodes)
581585
582586
@@ -633,7 +637,7 @@ executeTransGraphRelationalExpr _ (InProcessConnection conf) tgraphExpr = excEit
633637 graph <- readTVar graphTvar
634638 case evalTransGraphRelationalExpr tgraphExpr graph of
635639 Left err -> pure (Left err)
636− Right relExpr -> case evalState (RE.evalRelationalExpr relExpr) (RE.mkRelationalExprState RE.emptyDatabaseContext) of
640+ Right relExpr -> case evalState (RE.evalRelationalExpr relExpr) (RES.mkRelationalExprState DCE.emptyDatabaseContext) of
637641 Left err -> pure (Left err)
638642 Right rel -> pure (force (Right rel))
639643executeTransGraphRelationalExpr sessionId conn@(RemoteProcessConnection _) tgraphExpr = remoteCall conn (ExecuteTransGraphRelationalExpr sessionId tgraphExpr)
@@ -646,12 +650,12 @@ executeSchemaExpr sessionId (InProcessConnection conf) schemaExpr = atomically $
646650 Left err -> pure (Just err)
647651 Right (session, _) -> do
648652 let subschemas' = subschemas session
649− case Schema.evalSchemaExpr schemaExpr (Sess.concreteDatabaseContext session) subschemas' of
650− Left err -> pure (Just err)
651− Right (newSubschemas, newContext) -> do
653+ case runState (Schema.evalSchemaExpr schemaExpr subschemas') (defaultDatabaseContextEvalState (Sess.concreteDatabaseContext session)) of
654+ (Left err, _) -> pure (Just err)
655+ (Right newSubschemas, evaldState) -> do
652656 --hm- maybe we should start using lenses
653657 let discon = Sess.disconnectedTransaction session
654− newSchemas = Schemas newContext newSubschemas
658+ newSchemas = Schemas (dbcontext evaldState) newSubschemas
655659 newSession = Session (DisconnectedTransaction (Discon.parentId discon) newSchemas) (Sess.schemaName session)
656660 STMMap.insert newSession sessionId sessions
657661 pure Nothing
@@ -696,7 +700,7 @@ typeForRelationalExprSTM sessionId (InProcessConnection conf) relExpr = do
696700 Schema.processRelationalExprInSchema schema relExpr
697701 case processed of
698702 Left err -> pure (Left err)
699− Right relExpr' -> pure $ evalState (RE.typeForRelationalExpr relExpr') (RE.mkRelationalExprState (Sess.concreteDatabaseContext session))
703+ Right relExpr' -> pure $ evalState (RE.typeForRelationalExpr relExpr') (RES.mkRelationalExprState (Sess.concreteDatabaseContext session))
700704
701705typeForRelationalExprSTM _ _ _ = error "typeForRelationalExprSTM called on non-local connection"
702706
@@ -709,11 +713,11 @@ inclusionDependencies sessionId (InProcessConnection conf) = do
709713 case eSession of
710714 Left err -> pure $ Left err
711715 Right (session, schema) -> do
712− let context = Sess.concreteDatabaseContext session
716+ let context' = Sess.concreteDatabaseContext session
713717 if schemaName session == defaultSchemaName then
714− pure $ Right (B.inclusionDependencies context)
718+ pure $ Right (B.inclusionDependencies context')
715719 else
716− pure (Schema.inclusionDependenciesInSchema schema (B.inclusionDependencies context))
720+ pure (Schema.inclusionDependenciesInSchema schema (B.inclusionDependencies context'))
717721
718722inclusionDependencies sessionId conn@(RemoteProcessConnection _) = remoteCall conn (RetrieveInclusionDependencies sessionId)
719723
@@ -726,7 +730,7 @@ planForDatabaseContextExpr sessionId (InProcessConnection conf) dbExpr = do
726730 case eSession of
727731 Left err -> pure $ Left err
728732 Right (session, _) -> if schemaName session == defaultSchemaName then
729− pure $ evalState (applyStaticDatabaseOptimization dbExpr) (Sess.concreteDatabaseContext session)
733+ pure $ evalState (applyStaticDatabaseOptimization dbExpr) (defaultDatabaseContextEvalState (Sess.concreteDatabaseContext session))
730734 else -- don't show any optimization because the current optimization infrastructure relies on access to the base context- this probably underscores the need for each schema to have its own DatabaseContext, even if it is generated on-the-fly
731735 pure (Right dbExpr)
732736
@@ -759,11 +763,11 @@ relationVariablesAsRelation sessionId (InProcessConnection conf) = do
759763 case eSession of
760764 Left err -> pure (Left err)
761765 Right (session, schema) -> do
762− let context = Sess.concreteDatabaseContext session
766+ let context' = Sess.concreteDatabaseContext session
763767 if Sess.schemaName session == defaultSchemaName then
764− pure $ R.relationVariablesAsRelation (relationVariables context)
768+ pure $ R.relationVariablesAsRelation (relationVariables context')
765769 else
766− case Schema.relationVariablesInSchema schema context of
770+ case Schema.relationVariablesInSchema schema context' of
767771 Left err -> pure (Left err)
768772 Right relvars -> pure $ R.relationVariablesAsRelation relvars
769773
@@ -831,3 +835,10 @@ disconnectedTransaction_ sessionId (InProcessConnection conf) = do
831835 Nothing -> error "No such session"
832836 Just (Sess.Session discon _) -> pure discon
833837disconnectedTransaction_ _ _= error "remote connection used"
838+
839+--includes standard constraint validator
840+defaultDatabaseContextEvalState :: DatabaseContext -> DatabaseContextEvalState
841+defaultDatabaseContextEvalState context = DatabaseContextEvalState {
842+ dbcontext = context,
843+ constraintValidator = checkConstraints
844+ }
src/lib/ProjectM36/DatabaseContext.hs+8
@@ -5,6 +5,7 @@ import qualified Data.HashSet as HS
55import ProjectM36.DataTypes.Basic
66import ProjectM36.AtomFunctions.Basic
77import ProjectM36.Relation
8+import ProjectM36.TupleSet
89
910empty :: DatabaseContext
1011empty = DatabaseContext { inclusionDependencies = M.empty,
@@ -30,3 +31,10 @@ basicDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
3031 notifications = M.empty,
3132 typeConstructorMapping = basicTypeConstructorMapping
3233 }
34+
35+--returns a database context with all tuples removed
36+--this is useful for type checking and optimization
37+contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
38+contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
39+ where
40+ relVars = M.map (\rel -> Relation (attributes rel) emptyTupleSet) (relationVariables contextIn)
src/lib/ProjectM36/DatabaseContextExpression.hsnew file+281
@@ -0,0 +1,281 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+module ProjectM36.DatabaseContextExpression where
3+import ProjectM36.RelationalExpression
4+import ProjectM36.RelationalExpressionState
5+import ProjectM36.Relation
6+import ProjectM36.Base
7+import ProjectM36.Error
8+import ProjectM36.AtomFunctionBody
9+import ProjectM36.TupleSet
10+import ProjectM36.Tuple
11+import ProjectM36.AtomType
12+import ProjectM36.AtomFunction
13+import qualified ProjectM36.Attribute as A
14+import qualified Data.HashSet as HS
15+import Control.Exception
16+import Data.Char (isUpper)
17+import qualified Data.Text as T
18+import qualified ProjectM36.TypeConstructorDef as TCD
19+import Control.Monad.State
20+import qualified Data.Map as M
21+import Data.Either
22+import Data.Maybe
23+
24+import GHC
25+import GHC.Paths
26+
27+type ConstraintValidator = DatabaseContextExpr -> DatabaseContext -> Maybe RelationalError
28+
29+data DatabaseContextEvalState = DatabaseContextEvalState {
30+ dbcontext :: DatabaseContext,
31+ constraintValidator :: ConstraintValidator
32+ }
33+
34+type DatabaseState a = State DatabaseContextEvalState a
35+
36+getDatabaseContext :: DatabaseState DatabaseContext
37+getDatabaseContext = liftM dbcontext get
38+
39+getConstraintValidator :: DatabaseState ConstraintValidator
40+getConstraintValidator = liftM constraintValidator get
41+
42+putDatabaseContext :: DatabaseContext -> DatabaseState ()
43+putDatabaseContext context = do
44+ dbstate <- get
45+ put (dbstate {dbcontext = context})
46+
47+emptyDatabaseContext :: DatabaseContext
48+emptyDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
49+ relationVariables = M.empty,
50+ atomFunctions = HS.empty,
51+ notifications = M.empty,
52+ typeConstructorMapping = []
53+ }
54+
55+
56+
57+--helper function to process relation variable creation/assignment
58+--the dbcontextexpr is useful for optimizing the constraint validation
59+setRelVar :: DatabaseContextExpr -> RelVarName -> Relation -> DatabaseState (Maybe RelationalError)
60+setRelVar expr relVarName rel = do
61+ currentContext <- getDatabaseContext
62+ let newRelVars = M.insert relVarName rel $ relationVariables currentContext
63+ potentialContext = currentContext { relationVariables = newRelVars }
64+ validateConstraints expr potentialContext
65+
66+validateConstraints :: DatabaseContextExpr -> DatabaseContext -> DatabaseState (Maybe RelationalError)
67+validateConstraints expr potentialContext = do
68+ validator <- getConstraintValidator
69+ case validator expr potentialContext of
70+ Just err -> pure (Just err)
71+ Nothing -> do
72+ putDatabaseContext potentialContext
73+ pure Nothing
74+
75+-- it is not an error to delete a relvar which does not exist, just like it is not an error to insert a pre-existing tuple into a relation
76+deleteRelVar :: DatabaseContextExpr -> RelVarName -> DatabaseState (Maybe RelationalError)
77+deleteRelVar expr relVarName = do
78+ currContext <- getDatabaseContext
79+ let newRelVars = M.delete relVarName (relationVariables currContext)
80+ newContext = currContext { relationVariables = newRelVars }
81+ validateConstraints expr newContext
82+
83+evalContextExpr :: DatabaseContextExpr -> DatabaseState (Maybe RelationalError)
84+evalContextExpr NoOperation = pure Nothing
85+
86+evalContextExpr expr@(Define relVarName attrExprs) = do
87+ relvars <- liftM relationVariables getDatabaseContext
88+ tConss <- liftM typeConstructorMapping getDatabaseContext
89+ let eAttrs = map (evalAttrExpr tConss) attrExprs
90+ case lefts eAttrs of
91+ err:_ -> pure (Just err)
92+ [] -> case M.member relVarName relvars of
93+ True -> return (Just (RelVarAlreadyDefinedError relVarName))
94+ False -> setRelVar expr relVarName emptyRelation
95+ where
96+ attrs = A.attributesFromList (rights eAttrs)
97+ emptyRelation = Relation attrs emptyTupleSet
98+
99+evalContextExpr expr@(Undefine relVarName) = do
100+ deleteRelVar expr relVarName
101+
102+evalContextExpr ex@(Assign relVarName expr) = do
103+ -- in the future, it would be nice to get types from the RelationalExpr instead of needing to evaluate it
104+ context <- getDatabaseContext
105+ let existingRelVar = M.lookup relVarName relVarTable
106+ relVarTable = relationVariables context
107+ value = evalState (evalRelationalExpr expr) (RelationalExprStateElems context)
108+ case value of
109+ Left err -> return $ Just err
110+ Right rel -> case existingRelVar of
111+ Nothing -> setRelVar ex relVarName rel
112+ Just existingRel -> let expectedAttributes = attributes existingRel
113+ foundAttributes = attributes rel in
114+ if A.attributesEqual expectedAttributes foundAttributes then
115+ setRelVar ex relVarName rel
116+ else
117+ return $ Just (RelVarAssignmentTypeMismatchError expectedAttributes foundAttributes)
118+
119+evalContextExpr (Insert relVarName relExpr) = evalContextExpr $ Assign relVarName (Union relExpr (RelationVariable relVarName ()))
120+
121+evalContextExpr expr@(Delete relVarName predicate) = do
122+ context <- getDatabaseContext
123+ let updatedRel = evalState (evalRelationalExpr (Restrict (NotPredicate predicate) (RelationVariable relVarName ()))) (RelationalExprStateElems context)
124+ case updatedRel of
125+ Left err -> return $ Just err
126+ Right rel -> setRelVar expr relVarName rel
127+
128+--union of restricted+updated portion and the unrestricted+unupdated portion
129+evalContextExpr expr@(Update relVarName atomExprMap restrictionPredicateExpr) = do
130+ context <- getDatabaseContext
131+ let relVarTable = relationVariables context
132+ case M.lookup relVarName relVarTable of
133+ Nothing -> return $ Just (RelVarNotDefinedError relVarName)
134+ Just rel -> do
135+ case evalState (predicateRestrictionFilter (attributes rel) restrictionPredicateExpr) (RelationalExprStateElems context) of
136+ Left err -> return $ Just err
137+ Right predicateFunc -> do
138+ case makeUpdatedRel rel of
139+ Left err -> return $ Just err
140+ Right updatedRel -> setRelVar expr relVarName updatedRel
141+ where
142+ makeUpdatedRel relin = do
143+ restrictedPortion <- restrict predicateFunc relin
144+ unrestrictedPortion <- restrict (not . predicateFunc) relin
145+ updatedPortion <- relMap (updateTupleWithAtomExprs atomExprMap context) restrictedPortion
146+ union updatedPortion unrestrictedPortion
147+
148+evalContextExpr expr@(AddInclusionDependency newDepName newDep) = do
149+ currContext <- getDatabaseContext
150+ let currDeps = inclusionDependencies currContext
151+ newDeps = M.insert newDepName newDep currDeps
152+ if M.member newDepName currDeps then
153+ return $ Just (InclusionDependencyNameInUseError newDepName)
154+ else do
155+ let potentialContext = currContext { inclusionDependencies = newDeps }
156+ validateConstraints expr potentialContext
157+
158+evalContextExpr (RemoveInclusionDependency depName) = do
159+ currContext <- getDatabaseContext
160+ let currDeps = inclusionDependencies currContext
161+ newDeps = M.delete depName currDeps
162+ if M.notMember depName currDeps then
163+ return $ Just (InclusionDependencyNameNotInUseError depName)
164+ else do
165+ putDatabaseContext $ currContext {inclusionDependencies = newDeps }
166+ return Nothing
167+
168+-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
169+evalContextExpr (AddNotification notName triggerExpr resultExpr) = do
170+ currentContext <- getDatabaseContext
171+ let nots = notifications currentContext
172+ if M.member notName nots then
173+ return $ Just (NotificationNameInUseError notName)
174+ else do
175+ let newNotifications = M.insert notName newNotification nots
176+ newNotification = Notification { changeExpr = triggerExpr,
177+ reportExpr = resultExpr }
178+ putDatabaseContext $ currentContext { notifications = newNotifications }
179+ return Nothing
180+
181+evalContextExpr (RemoveNotification notName) = do
182+ currentContext <- getDatabaseContext
183+ let nots = notifications currentContext
184+ if M.notMember notName nots then
185+ return $ Just (NotificationNameNotInUseError notName)
186+ else do
187+ let newNotifications = M.delete notName nots
188+ putDatabaseContext $ currentContext { notifications = newNotifications }
189+ return Nothing
190+
191+-- | Adds type and data constructors to the database context.
192+-- validate that the type *and* constructor names are unique! not yet implemented!
193+evalContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
194+ currentContext <- getDatabaseContext
195+ let oldTypes = typeConstructorMapping currentContext
196+ tConsName = TCD.name tConsDef
197+ -- validate that the constructor's types exist
198+ case validateTypeConstructorDef tConsDef dConsDefList of
199+ errs@(_:_) -> pure $ Just (someErrors errs)
200+ [] -> do
201+ if T.length tConsName < 1 || not (isUpper (T.head tConsName)) then
202+ pure $ Just (InvalidAtomTypeName tConsName)
203+ else if isJust (findTypeConstructor tConsName oldTypes) then
204+ pure $ Just (AtomTypeNameInUseError tConsName)
205+ else do
206+ let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
207+ putDatabaseContext $ currentContext { typeConstructorMapping = newTypes }
208+ pure Nothing
209+
210+-- | Removing the atom constructor prevents new atoms of the type from being created. Existing atoms of the type remain. Thus, the atomTypes list in the DatabaseContext need not be all-inclusive.
211+evalContextExpr (RemoveTypeConstructor tConsName) = do
212+ currentContext <- getDatabaseContext
213+ let oldTypes = typeConstructorMapping currentContext
214+ if findTypeConstructor tConsName oldTypes == Nothing then
215+ pure $ Just (AtomTypeNameNotInUseError tConsName)
216+ else do
217+ let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
218+ putDatabaseContext $ currentContext { typeConstructorMapping = newTypes }
219+ pure Nothing
220+
221+evalContextExpr (MultipleExpr exprs) = do
222+ --the multiple expressions must pass the same context around- not the old unmodified context
223+ evald <- forM exprs evalContextExpr
224+ --some lifting magic needed here
225+ case catMaybes evald of
226+ [] -> return $ Nothing
227+ err:_ -> return $ Just err
228+
229+evalContextExpr (RemoveAtomFunction funcName) = do
230+ currentContext <- getDatabaseContext
231+ let atomFuncs = atomFunctions currentContext
232+ dudFunc = emptyAtomFunction funcName -- just for lookup in the hashset
233+ if HS.member dudFunc atomFuncs then do
234+ let updatedFuncs = HS.delete dudFunc atomFuncs
235+ putDatabaseContext (currentContext {atomFunctions = updatedFuncs })
236+ pure Nothing
237+ else
238+ pure (Just (AtomFunctionNameNotInUseError funcName))
239+
240+evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
241+evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) = do
242+ case mScriptSession of
243+ Nothing -> pure (Left (AtomFunctionBodyScriptError ScriptCompilationDisabledError))
244+ Just scriptSession -> do
245+ res <- try $ runGhc (Just libdir) $ do
246+ setSession (hscEnv scriptSession)
247+ let atomFuncs = atomFunctions currentContext
248+ --compile the function
249+ eCompiledFunc <- compileAtomFunctionScript scriptSession script
250+ pure $ case eCompiledFunc of
251+ Left err -> Left (AtomFunctionBodyScriptError err)
252+ Right compiledFunc -> do
253+ funcAtomType <- mapM (\funcTypeArg -> atomTypeForTypeConstructor funcTypeArg (typeConstructorMapping currentContext)) funcType
254+ let updatedFuncs = HS.insert newAtomFunc atomFuncs
255+ newContext = currentContext { atomFunctions = updatedFuncs }
256+ newAtomFunc = AtomFunction { atomFuncName = funcName,
257+ atomFuncType = funcAtomType,
258+ atomFuncBody = AtomFunctionBody (Just script) compiledFunc }
259+
260+ -- check if the name is already in use
261+ if HS.member funcName (HS.map atomFuncName atomFuncs) then
262+ Left (AtomFunctionNameInUseError funcName)
263+ else do
264+ Right newContext
265+ case res of
266+ Left (exc :: SomeException) -> pure $ Left (AtomFunctionBodyScriptError (OtherScriptCompilationError (show exc)))
267+ Right eContext -> case eContext of
268+ Left err -> pure (Left err)
269+ Right context' -> pure (Right context')
270+
271+updateTupleWithAtomExprs :: (M.Map AttributeName AtomExpr) -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
272+updateTupleWithAtomExprs exprMap context tupIn = do
273+ --resolve all atom exprs
274+ atomsAssoc <- mapM (\(attrName, atomExpr) -> do
275+ atom <- evalState (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
276+ pure (attrName, atom)
277+ ) (M.toList exprMap)
278+ pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
279+
280+-- the type of a relational expression is equal to the relation attribute set returned from executing the relational expression; therefore, the type can be cheaply derived by evaluating a relational expression and ignoring and tuple processing
281+-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
src/lib/ProjectM36/InclusionDependency.hs−1
@@ -5,7 +5,6 @@ import ProjectM36.Error
55import ProjectM36.Relation
66import qualified Data.Map as M
77import qualified Data.Text as T
8−import qualified Data.Set as S
98
109inclusionDependenciesAsRelation :: InclusionDependencies -> Either RelationalError Relation
1110inclusionDependenciesAsRelation incDeps = do
src/lib/ProjectM36/InclusionDependencyValidation.hsnew file+30
@@ -0,0 +1,30 @@
1+module ProjectM36.InclusionDependencyValidation where
2+import ProjectM36.Base
3+import ProjectM36.Error
4+import ProjectM36.RelationalExpression
5+import ProjectM36.RelationalExpressionState
6+import ProjectM36.Relation
7+import ProjectM36.StaticOptimizer.InclusionDependencies
8+import qualified Data.Map as M
9+import Control.Monad.State
10+
11+--run verification on all constraints
12+checkConstraints :: DatabaseContextExpr -> DatabaseContext -> Maybe RelationalError
13+checkConstraints expr context = case optimizedIncDepMap of
14+ Left err -> Just err
15+ Right filteredIncDepMap -> case failures filteredIncDepMap of
16+ [] -> Nothing
17+ l -> Just (MultipleErrors l)
18+ where
19+ optimizedIncDepMap = filterInclusionDependenciesForValidation expr deps
20+ failures incDeps = M.elems $ M.mapMaybeWithKey checkIncDep incDeps
21+ deps = inclusionDependencies context
22+ eval expr' = evalState (evalRelationalExpr expr') (RelationalExprStateElems context)
23+ checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
24+ let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
25+ case eval checkExpr of
26+ Left err -> Just err
27+ Right resultRel -> if resultRel == relationTrue then
28+ Nothing
29+ else
30+ Just $ InclusionDependencyCheckError depName
src/lib/ProjectM36/IsomorphicSchema.hs+20−15
@@ -4,6 +4,8 @@ import ProjectM36.Base
44import ProjectM36.Error
55import ProjectM36.MiscUtils
66import ProjectM36.RelationalExpression
7+import ProjectM36.RelationalExpressionState
8+import ProjectM36.DatabaseContextExpression
79import ProjectM36.Relation
810import qualified ProjectM36.AttributeNames as AN
911import Control.Monad
@@ -316,25 +318,28 @@ createIncDepsForIsomorph sname (IsoRestrict _ predi (rvTrue, rvFalse)) = let
316318createIncDepsForIsomorph _ _ = M.empty
317319
318320-- in the case of IsoRestrict, the database context should be updated with the restriction so that if the restriction does not hold, then the schema cannot be created
319−evalSchemaExpr :: SchemaExpr -> DatabaseContext -> Subschemas -> Either RelationalError (Subschemas, DatabaseContext)
320−evalSchemaExpr (AddSubschema sname morphs) context sschemas = do
321+evalSchemaExpr :: SchemaExpr -> Subschemas -> DatabaseState (Either RelationalError Subschemas)
322+evalSchemaExpr (AddSubschema sname morphs) sschemas = do
323+ context <- getDatabaseContext
324+ let valid = validateSchema newSchema context
325+ newSchema = Schema morphs
326+
321327 if M.member sname sschemas then
322− Left (SubschemaNameInUseError sname)
328+ pure (Left (SubschemaNameInUseError sname))
323329 else case valid of
324− Just err -> Left (SchemaCreationError err)
325− Nothing ->
330+ Just err -> pure (Left (SchemaCreationError err))
331+ Nothing -> do
326332 let newSchemas = M.insert sname newSchema sschemas
327333 moreIncDeps = foldr (\morph acc -> M.union acc (createIncDepsForIsomorph sname morph)) M.empty morphs
328334 incDepExprs = MultipleExpr (map (uncurry AddInclusionDependency) (M.toList moreIncDeps))
329− in
330− case runState (evalContextExpr incDepExprs) context of
331− (Just err, _) -> Left err
332− (Nothing, newContext) -> pure (newSchemas, newContext)
333− where
334− newSchema = Schema morphs
335− valid = validateSchema newSchema context
336−evalSchemaExpr (RemoveSubschema sname) context sschemas = if M.member sname sschemas then
337− pure (M.delete sname sschemas, context)
335+ mErr <- evalContextExpr incDepExprs
336+ case mErr of
337+ Just err -> pure (Left err)
338+ Nothing -> pure (Right newSchemas)
339+
340+
341+evalSchemaExpr (RemoveSubschema sname) sschemas = if M.member sname sschemas then
342+ pure (Right (M.delete sname sschemas))
338343 else
339− Left (SubschemaNameNotInUseError sname)
344+ pure (Left (SubschemaNameNotInUseError sname))
340345
src/lib/ProjectM36/Notifications.hs+1
@@ -1,6 +1,7 @@
11module ProjectM36.Notifications where
22import ProjectM36.Base
33import ProjectM36.RelationalExpression
4+import ProjectM36.RelationalExpressionState
45import Control.Monad.State
56import qualified Data.Map as M
67import Data.Either (isRight)
src/lib/ProjectM36/RelationalExpression.hs+3−314
@@ -2,71 +2,23 @@
22module ProjectM36.RelationalExpression where
33import ProjectM36.Relation
44import ProjectM36.Tuple
5−import ProjectM36.TupleSet
65import ProjectM36.Base
76import ProjectM36.Error
87import ProjectM36.AtomType
9−import ProjectM36.AtomFunctionBody
108import ProjectM36.DataTypes.Primitive
119import ProjectM36.AtomFunction
12−import ProjectM36.StaticOptimizer
10+import ProjectM36.RelationalExpressionState
11+import ProjectM36.DatabaseContext
1312import qualified ProjectM36.Attribute as A
1413import qualified Data.Map as M
15−import qualified Data.HashSet as HS
1614import qualified Data.Set as S
1715import Control.Monad.State hiding (join)
18−import Control.Exception
1916import Data.Maybe
2017import Data.Either
21−import Data.Char (isUpper)
22−import qualified Data.Text as T
18+
2319import qualified Data.Vector as V
24−import qualified ProjectM36.TypeConstructorDef as TCD
2520import Control.Monad.Trans.Except
2621
27−import GHC
28−import GHC.Paths
29−
30−import Debug.Trace
31−
32−-- we need to pass around a higher level RelationTuple and Attributes in order to solve #52
33−data RelationalExprStateElems = RelationalExprStateTupleElems DatabaseContext RelationTuple | -- used when fully evaluating a relexpr
34− RelationalExprStateAttrsElems DatabaseContext Attributes | --used when evaluating the type of a relexpr
35− RelationalExprStateElems DatabaseContext --used by default at the top level of evaluation
36−
37−instance Show RelationalExprStateElems where
38− show (RelationalExprStateTupleElems _ tup) = "RelationalExprStateTupleElems " ++ show tup
39− show (RelationalExprStateAttrsElems _ attrs) = "RelationalExprStateAttrsElems" ++ show attrs
40− show (RelationalExprStateElems _) = "RelationalExprStateElems"
41−
42−mkRelationalExprState :: DatabaseContext -> RelationalExprStateElems
43−mkRelationalExprState ctx = RelationalExprStateElems ctx
44−
45−mergeTuplesIntoRelationalExprState :: RelationTuple -> RelationalExprStateElems -> RelationalExprStateElems
46−mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateElems ctx) = RelationalExprStateTupleElems ctx tupIn
47−mergeTuplesIntoRelationalExprState _ st@(RelationalExprStateAttrsElems _ _) = st
48−mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateTupleElems ctx existingTuple) = let mergedTupMap = M.union (tupleToMap tupIn) (tupleToMap existingTuple) in
49− RelationalExprStateTupleElems ctx (mkRelationTupleFromMap mergedTupMap)
50−
51−mergeAttributesIntoRelationalExprState :: Attributes -> RelationalExprStateElems -> RelationalExprStateElems
52−mergeAttributesIntoRelationalExprState attrs (RelationalExprStateElems ctx) = RelationalExprStateAttrsElems ctx attrs
53−mergeAttributesIntoRelationalExprState _ st@(RelationalExprStateTupleElems _ _) = st
54−mergeAttributesIntoRelationalExprState attrsIn (RelationalExprStateAttrsElems ctx attrs) = RelationalExprStateAttrsElems ctx (A.union attrsIn attrs)
55−
56−type DatabaseState a = State DatabaseContext a
57−
58−type RelationalExprState a = State RelationalExprStateElems a
59−
60−stateContext :: RelationalExprStateElems -> DatabaseContext
61−stateContext (RelationalExprStateTupleElems ctx _) = ctx
62−stateContext (RelationalExprStateElems ctx) = ctx
63−stateContext (RelationalExprStateAttrsElems ctx _) = ctx
64−
65−setStateContext :: RelationalExprStateElems -> DatabaseContext -> RelationalExprStateElems
66−setStateContext (RelationalExprStateTupleElems _ tup) ctx = RelationalExprStateTupleElems ctx tup
67−setStateContext (RelationalExprStateElems _) ctx = RelationalExprStateElems ctx
68−setStateContext (RelationalExprStateAttrsElems _ attrs) ctx = RelationalExprStateAttrsElems ctx attrs
69−
7022--relvar state is needed in evaluation of relational expression but only as read-only in order to extract current relvar values
7123evalRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
7224evalRelationalExpr (RelationVariable name _) = do
@@ -189,262 +141,6 @@ evalRelationalExpr (Extend tupleExpression relExpr) = do
189141 Left err -> pure (Left err)
190142 Right (newAttrs, tupProc') -> pure $ relMogrify tupProc' newAttrs rel
191143
192−emptyDatabaseContext :: DatabaseContext
193−emptyDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
194− relationVariables = M.empty,
195− atomFunctions = HS.empty,
196− notifications = M.empty,
197− typeConstructorMapping = []
198− }
199−
200−
201−
202−--helper function to process relation variable creation/assignment
203−setRelVar :: RelVarName -> Relation -> DatabaseState (Maybe RelationalError)
204−setRelVar relVarName rel = do
205− currentContext <- get
206− let newRelVars = M.insert relVarName rel $ relationVariables currentContext
207− potentialContext = currentContext { relationVariables = newRelVars }
208−
209− case checkConstraints potentialContext of
210− Just err -> return $ Just err
211− Nothing -> do
212− put potentialContext
213− return Nothing
214−
215−-- it is not an error to delete a relvar which does not exist, just like it is not an error to insert a pre-existing tuple into a relation
216−deleteRelVar :: RelVarName -> DatabaseState (Maybe RelationalError)
217−deleteRelVar relVarName = do
218− currContext <- get
219− let newRelVars = M.delete relVarName (relationVariables currContext)
220− newContext = currContext { relationVariables = newRelVars }
221− put newContext
222− return Nothing
223−
224−evalContextExpr :: DatabaseContextExpr -> DatabaseState (Maybe RelationalError)
225−evalContextExpr NoOperation = pure Nothing
226−
227−evalContextExpr (Define relVarName attrExprs) = do
228− relvars <- liftM relationVariables get
229− tConss <- liftM typeConstructorMapping get
230− let eAttrs = map (evalAttrExpr tConss) attrExprs
231− case lefts eAttrs of
232− err:_ -> pure (Just err)
233− [] -> case M.member relVarName relvars of
234− True -> return (Just (RelVarAlreadyDefinedError relVarName))
235− False -> setRelVar relVarName emptyRelation
236− where
237− attrs = A.attributesFromList (rights eAttrs)
238− emptyRelation = Relation attrs emptyTupleSet
239−
240−evalContextExpr (Undefine relVarName) = do
241− deleteRelVar relVarName
242−
243−evalContextExpr (Assign relVarName expr) = do
244− -- in the future, it would be nice to get types from the RelationalExpr instead of needing to evaluate it
245− context <- get
246− let existingRelVar = M.lookup relVarName relVarTable
247− relVarTable = relationVariables context
248− value = evalState (evalRelationalExpr expr) (RelationalExprStateElems context)
249− case value of
250− Left err -> return $ Just err
251− Right rel -> case existingRelVar of
252− Nothing -> setRelVar relVarName rel
253− Just existingRel -> let expectedAttributes = attributes existingRel
254− foundAttributes = attributes rel in
255− if A.attributesEqual expectedAttributes foundAttributes then
256− setRelVar relVarName rel
257− else
258− return $ Just (RelVarAssignmentTypeMismatchError expectedAttributes foundAttributes)
259−
260−evalContextExpr (Insert relVarName relExpr) = evalContextExpr $ Assign relVarName (Union relExpr (RelationVariable relVarName ()))
261−
262−evalContextExpr (Delete relVarName predicate) = do
263− context <- get
264− let updatedRel = evalState (evalRelationalExpr (Restrict (NotPredicate predicate) (RelationVariable relVarName ()))) (RelationalExprStateElems context)
265− case updatedRel of
266− Left err -> return $ Just err
267− Right rel -> setRelVar relVarName rel
268−
269−--union of restricted+updated portion and the unrestricted+unupdated portion
270−evalContextExpr (Update relVarName atomExprMap restrictionPredicateExpr) = do
271− context <- get
272− let relVarTable = relationVariables context
273− case M.lookup relVarName relVarTable of
274− Nothing -> return $ Just (RelVarNotDefinedError relVarName)
275− Just rel -> do
276− case evalState (predicateRestrictionFilter (attributes rel) restrictionPredicateExpr) (RelationalExprStateElems context) of
277− Left err -> return $ Just err
278− Right predicateFunc -> do
279− case makeUpdatedRel rel of
280− Left err -> return $ Just err
281− Right updatedRel -> setRelVar relVarName updatedRel
282− where
283− makeUpdatedRel relin = do
284− restrictedPortion <- restrict predicateFunc relin
285− unrestrictedPortion <- restrict (not . predicateFunc) relin
286− updatedPortion <- relMap (updateTupleWithAtomExprs atomExprMap context) restrictedPortion
287− union updatedPortion unrestrictedPortion
288−
289−evalContextExpr (AddInclusionDependency newDepName newDep) = do
290− currContext <- get
291− let currDeps = inclusionDependencies currContext
292− newDeps = M.insert newDepName newDep currDeps
293− if M.member newDepName currDeps then
294− return $ Just (InclusionDependencyNameInUseError newDepName)
295− else do
296− let potentialContext = currContext { inclusionDependencies = newDeps }
297− case checkConstraints potentialContext of
298− Just err -> return $ Just err
299− Nothing -> do
300− put potentialContext
301− return Nothing
302−
303−evalContextExpr (RemoveInclusionDependency depName) = do
304− currContext <- get
305− let currDeps = inclusionDependencies currContext
306− newDeps = M.delete depName currDeps
307− if M.notMember depName currDeps then
308− return $ Just (InclusionDependencyNameNotInUseError depName)
309− else do
310− put $ currContext {inclusionDependencies = newDeps }
311− return Nothing
312−
313−-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
314−evalContextExpr (AddNotification notName triggerExpr resultExpr) = do
315− currentContext <- get
316− let nots = notifications currentContext
317− if M.member notName nots then
318− return $ Just (NotificationNameInUseError notName)
319− else do
320− let newNotifications = M.insert notName newNotification nots
321− newNotification = Notification { changeExpr = triggerExpr,
322− reportExpr = resultExpr }
323− put $ currentContext { notifications = newNotifications }
324− return Nothing
325−
326−evalContextExpr (RemoveNotification notName) = do
327− currentContext <- get
328− let nots = notifications currentContext
329− if M.notMember notName nots then
330− return $ Just (NotificationNameNotInUseError notName)
331− else do
332− let newNotifications = M.delete notName nots
333− put $ currentContext { notifications = newNotifications }
334− return Nothing
335−
336−-- | Adds type and data constructors to the database context.
337−-- validate that the type *and* constructor names are unique! not yet implemented!
338−evalContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
339− currentContext <- get
340− let oldTypes = typeConstructorMapping currentContext
341− tConsName = TCD.name tConsDef
342− -- validate that the constructor's types exist
343− case validateTypeConstructorDef tConsDef dConsDefList of
344− errs@(_:_) -> pure $ Just (someErrors errs)
345− [] -> do
346− if T.length tConsName < 1 || not (isUpper (T.head tConsName)) then
347− pure $ Just (InvalidAtomTypeName tConsName)
348− else if isJust (findTypeConstructor tConsName oldTypes) then
349− pure $ Just (AtomTypeNameInUseError tConsName)
350− else do
351− let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
352− put $ currentContext { typeConstructorMapping = newTypes }
353− pure Nothing
354−
355−-- | Removing the atom constructor prevents new atoms of the type from being created. Existing atoms of the type remain. Thus, the atomTypes list in the DatabaseContext need not be all-inclusive.
356−evalContextExpr (RemoveTypeConstructor tConsName) = do
357− currentContext <- get
358− let oldTypes = typeConstructorMapping currentContext
359− if findTypeConstructor tConsName oldTypes == Nothing then
360− pure $ Just (AtomTypeNameNotInUseError tConsName)
361− else do
362− let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
363− put $ currentContext { typeConstructorMapping = newTypes }
364− pure Nothing
365−
366−evalContextExpr (MultipleExpr exprs) = do
367− --the multiple expressions must pass the same context around- not the old unmodified context
368− evald <- forM exprs evalContextExpr
369− --some lifting magic needed here
370− case catMaybes evald of
371− [] -> return $ Nothing
372− err:_ -> return $ Just err
373−
374−evalContextExpr (RemoveAtomFunction funcName) = do
375− currentContext <- get
376− let atomFuncs = atomFunctions currentContext
377− dudFunc = emptyAtomFunction funcName -- just for lookup in the hashset
378− if HS.member dudFunc atomFuncs then do
379− let updatedFuncs = HS.delete dudFunc atomFuncs
380− put (currentContext {atomFunctions = updatedFuncs })
381− pure Nothing
382− else
383− pure (Just (AtomFunctionNameNotInUseError funcName))
384−
385−evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
386−evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) = do
387− case mScriptSession of
388− Nothing -> pure (Left (AtomFunctionBodyScriptError ScriptCompilationDisabledError))
389− Just scriptSession -> do
390− res <- try $ runGhc (Just libdir) $ do
391− setSession (hscEnv scriptSession)
392− let atomFuncs = atomFunctions currentContext
393− --compile the function
394− eCompiledFunc <- compileAtomFunctionScript scriptSession script
395− pure $ case eCompiledFunc of
396− Left err -> Left (AtomFunctionBodyScriptError err)
397− Right compiledFunc -> do
398− funcAtomType <- mapM (\funcTypeArg -> atomTypeForTypeConstructor funcTypeArg (typeConstructorMapping currentContext)) funcType
399− let updatedFuncs = HS.insert newAtomFunc atomFuncs
400− newContext = currentContext { atomFunctions = updatedFuncs }
401− newAtomFunc = AtomFunction { atomFuncName = funcName,
402− atomFuncType = funcAtomType,
403− atomFuncBody = AtomFunctionBody (Just script) compiledFunc }
404−
405− -- check if the name is already in use
406− if HS.member funcName (HS.map atomFuncName atomFuncs) then
407− Left (AtomFunctionNameInUseError funcName)
408− else do
409− Right newContext
410− case res of
411− Left (exc :: SomeException) -> pure $ Left (AtomFunctionBodyScriptError (OtherScriptCompilationError (show exc)))
412− Right eContext -> case eContext of
413− Left err -> pure (Left err)
414− Right context' -> pure (Right context')
415−
416−updateTupleWithAtomExprs :: (M.Map AttributeName AtomExpr) -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
417−updateTupleWithAtomExprs exprMap context tupIn = do
418− --resolve all atom exprs
419− atomsAssoc <- mapM (\(attrName, atomExpr) -> do
420− atom <- evalState (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
421− pure (attrName, atom)
422− ) (M.toList exprMap)
423− pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
424−
425−--run verification on all constraints
426−checkConstraints :: DatabaseContext -> Maybe RelationalError
427−checkConstraints context = case optimizedIncDepMap of
428− Left err -> Just err
429− Right filteredIncDepMap -> case failures filteredIncDepMap of
430− [] -> Nothing
431− l -> Just (MultipleErrors l)
432− where
433− optimizedIncDepMap = filterInclusionDependenciesForValidation deps
434− failures incDeps = M.elems $ M.mapMaybeWithKey checkIncDep incDeps
435− deps = inclusionDependencies context
436− eval expr = evalState (evalRelationalExpr expr) (RelationalExprStateElems context)
437− checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
438− let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
439− case eval checkExpr of
440− Left err -> Just err
441− Right resultRel -> if resultRel == relationTrue then
442− Nothing
443− else
444− Just $ InclusionDependencyCheckError depName
445−
446−-- the type of a relational expression is equal to the relation attribute set returned from executing the relational expression; therefore, the type can be cheaply derived by evaluating a relational expression and ignoring and tuple processing
447−-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
448144
449145typeForRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
450146typeForRelationalExpr expr = do
@@ -455,13 +151,6 @@ typeForRelationalExpr expr = do
455151 rstate' = setStateContext rstate context'
456152 pure (evalState (evalRelationalExpr expr) rstate')
457153
458−--returns a database context with all tuples removed
459−--this is useful for type checking and optimization
460−contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
461−contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
462− where
463− relVars = M.map (\rel -> Relation (attributes rel) emptyTupleSet) (relationVariables contextIn)
464−
465154liftE :: (Monad m) => m (Either a b) -> ExceptT a m b
466155liftE v = do
467156 y <- lift v
src/lib/ProjectM36/RelationalExpressionState.hsnew file+42
@@ -0,0 +1,42 @@
1+module ProjectM36.RelationalExpressionState where
2+import ProjectM36.Base
3+import ProjectM36.Tuple
4+import Control.Monad.State
5+import qualified Data.Map as M
6+import qualified ProjectM36.Attribute as A
7+
8+type RelationalExprState a = State RelationalExprStateElems a
9+
10+-- we need to pass around a higher level RelationTuple and Attributes in order to solve #52
11+data RelationalExprStateElems = RelationalExprStateTupleElems DatabaseContext RelationTuple | -- used when fully evaluating a relexpr
12+ RelationalExprStateAttrsElems DatabaseContext Attributes | --used when evaluating the type of a relexpr
13+ RelationalExprStateElems DatabaseContext --used by default at the top level of evaluation
14+
15+instance Show RelationalExprStateElems where
16+ show (RelationalExprStateTupleElems _ tup) = "RelationalExprStateTupleElems " ++ show tup
17+ show (RelationalExprStateAttrsElems _ attrs) = "RelationalExprStateAttrsElems" ++ show attrs
18+ show (RelationalExprStateElems _) = "RelationalExprStateElems"
19+
20+mkRelationalExprState :: DatabaseContext -> RelationalExprStateElems
21+mkRelationalExprState ctx = RelationalExprStateElems ctx
22+
23+mergeTuplesIntoRelationalExprState :: RelationTuple -> RelationalExprStateElems -> RelationalExprStateElems
24+mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateElems ctx) = RelationalExprStateTupleElems ctx tupIn
25+mergeTuplesIntoRelationalExprState _ st@(RelationalExprStateAttrsElems _ _) = st
26+mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateTupleElems ctx existingTuple) = let mergedTupMap = M.union (tupleToMap tupIn) (tupleToMap existingTuple) in
27+ RelationalExprStateTupleElems ctx (mkRelationTupleFromMap mergedTupMap)
28+
29+mergeAttributesIntoRelationalExprState :: Attributes -> RelationalExprStateElems -> RelationalExprStateElems
30+mergeAttributesIntoRelationalExprState attrs (RelationalExprStateElems ctx) = RelationalExprStateAttrsElems ctx attrs
31+mergeAttributesIntoRelationalExprState _ st@(RelationalExprStateTupleElems _ _) = st
32+mergeAttributesIntoRelationalExprState attrsIn (RelationalExprStateAttrsElems ctx attrs) = RelationalExprStateAttrsElems ctx (A.union attrsIn attrs)
33+
34+stateContext :: RelationalExprStateElems -> DatabaseContext
35+stateContext (RelationalExprStateTupleElems ctx _) = ctx
36+stateContext (RelationalExprStateElems ctx) = ctx
37+stateContext (RelationalExprStateAttrsElems ctx _) = ctx
38+
39+setStateContext :: RelationalExprStateElems -> DatabaseContext -> RelationalExprStateElems
40+setStateContext (RelationalExprStateTupleElems _ tup) ctx = RelationalExprStateTupleElems ctx tup
41+setStateContext (RelationalExprStateElems _) ctx = RelationalExprStateElems ctx
42+setStateContext (RelationalExprStateAttrsElems _ attrs) ctx = RelationalExprStateAttrsElems ctx attrs
src/lib/ProjectM36/StaticOptimizer.hsdeleted−268
@@ -1,268 +0,0 @@
1−module ProjectM36.StaticOptimizer where
2−import ProjectM36.Base
3−--import ProjectM36.RelationalExpression
4−import ProjectM36.Relation
5−import ProjectM36.Error
6−import ProjectM36.InclusionDependency
7−import qualified ProjectM36.AttributeNames as AS
8−import ProjectM36.TupleSet
9−import Control.Monad.State hiding (join)
10−import Data.Either (rights, lefts)
11−import qualified Data.Set as S
12−import qualified Data.Map as M
13−
14−-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
15−-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
16−
17−--should optimizations offer the possibility to return errors? If they perform the up-front type-checking, maybe so
18−applyStaticRelationalOptimization :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
19−applyStaticRelationalOptimization e@(MakeStaticRelation _ _) = return $ Right e
20−
21−applyStaticRelationalOptimization e@(MakeRelationFromExprs _ _) = return $ Right e
22−
23−applyStaticRelationalOptimization e@(ExistingRelation _) = return $ Right e
24−
25−applyStaticRelationalOptimization e@(RelationVariable _ _) = return $ Right e
26−
27−--remove project of attributes which removes no attributes
28−applyStaticRelationalOptimization (Project attrNameSet expr) = do
29− relType <- typeForRelationalExpr expr
30− case relType of
31− Left err -> return $ Left err
32− Right relType2 -> if AS.all == attrNameSet then
33− applyStaticRelationalOptimization expr
34− else if AttributeNames (attributeNames relType2) == attrNameSet then
35− applyStaticRelationalOptimization expr
36− else do
37− optimizedSubExpression <- applyStaticRelationalOptimization expr
38− case optimizedSubExpression of
39− Left err -> return $ Left err
40− Right optSubExpr -> return $ Right $ Project attrNameSet optSubExpr
41−
42−applyStaticRelationalOptimization (Union exprA exprB) = do
43− optExprA <- applyStaticRelationalOptimization exprA
44− optExprB <- applyStaticRelationalOptimization exprB
45− case optExprA of
46− Left err -> return $ Left err
47− Right optExprAx -> case optExprB of
48− Left err -> return $ Left err
49− Right optExprBx -> if optExprAx == optExprBx then
50− return (Right optExprAx)
51− else
52− return $ Right $ Union optExprAx optExprBx
53−
54−applyStaticRelationalOptimization (Join exprA exprB) = do
55− optExprA <- applyStaticRelationalOptimization exprA
56− optExprB <- applyStaticRelationalOptimization exprB
57− case optExprA of
58− Left err -> return $ Left err
59− Right optExprA2 -> case optExprB of
60− Left err -> return $ Left err
61− Right optExprB2 -> if optExprA == optExprB then --A join A == A
62− return optExprA
63− else
64− return $ Right (Join optExprA2 optExprB2)
65−
66−applyStaticRelationalOptimization (Difference exprA exprB) = do
67− optExprA <- applyStaticRelationalOptimization exprA
68− optExprB <- applyStaticRelationalOptimization exprB
69− case optExprA of
70− Left err -> return $ Left err
71− Right optExprA2 -> case optExprB of
72− Left err -> return $ Left err
73− Right optExprB2 -> if optExprA == optExprB then do --A difference A == A where false
74− eEmptyRel <- typeForRelationalExpr optExprA2
75− case eEmptyRel of
76− Left err -> pure (Left err)
77− Right emptyRel -> pure (Right (ExistingRelation emptyRel))
78− else
79− return $ Right (Difference optExprA2 optExprB2)
80−
81−applyStaticRelationalOptimization e@(Rename _ _ _) = return $ Right e
82−
83−applyStaticRelationalOptimization (Group oldAttrNames newAttrName expr) = do
84− return $ Right $ Group oldAttrNames newAttrName expr
85−
86−applyStaticRelationalOptimization (Ungroup attrName expr) = do
87− return $ Right $ Ungroup attrName expr
88−
89−--remove restriction of nothing
90−applyStaticRelationalOptimization (Restrict predicate expr) = do
91− optimizedPredicate <- applyStaticPredicateOptimization predicate
92− case optimizedPredicate of
93− Left err -> return $ Left err
94− Right optimizedPredicate2 -> if optimizedPredicate2 == TruePredicate then
95− applyStaticRelationalOptimization expr
96− else if optimizedPredicate2 == NotPredicate TruePredicate then do
97− attributesRel <- typeForRelationalExpr expr
98− case attributesRel of
99− Left err -> return $ Left err
100− Right attributesRelA -> return $ Right $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
101− else do
102− optimizedSubExpression <- applyStaticRelationalOptimization expr
103− case optimizedSubExpression of
104− Left err -> return $ Left err
105− Right optSubExpr -> return $ Right $ Restrict optimizedPredicate2 optSubExpr
106−
107−applyStaticRelationalOptimization e@(Equals _ _) = return $ Right e
108−
109−applyStaticRelationalOptimization e@(NotEquals _ _) = return $ Right e
110−
111−applyStaticRelationalOptimization e@(Extend _ _) = return $ Right e
112−
113−applyStaticDatabaseOptimization :: DatabaseContextExpr -> DatabaseState (Either RelationalError DatabaseContextExpr)
114−applyStaticDatabaseOptimization x@NoOperation = pure $ Right x
115−applyStaticDatabaseOptimization x@(Define _ _) = pure $ Right x
116−
117−applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
118−
119−applyStaticDatabaseOptimization (Assign name expr) = do
120− context <- get
121− let optimizedExpr = evalState (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
122− case optimizedExpr of
123− Left err -> return $ Left err
124− Right optimizedExpr2 -> return $ Right (Assign name optimizedExpr2)
125−
126−applyStaticDatabaseOptimization (Insert name expr) = do
127− context <- get
128− let optimizedExpr = evalState (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
129− case optimizedExpr of
130− Left err -> return $ Left err
131− Right optimizedExpr2 -> return $ Right (Insert name optimizedExpr2)
132−
133−applyStaticDatabaseOptimization (Delete name predicate) = do
134− context <- get
135− let optimizedPredicate = evalState (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
136− case optimizedPredicate of
137− Left err -> return $ Left err
138− Right optimizedPredicate2 -> return $ Right (Delete name optimizedPredicate2)
139−
140−applyStaticDatabaseOptimization (Update name upmap predicate) = do
141− context <- get
142− let optimizedPredicate = evalState (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
143− case optimizedPredicate of
144− Left err -> return $ Left err
145− Right optimizedPredicate2 -> return $ Right (Update name upmap optimizedPredicate2)
146−
147−applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = return $ Right dep
148−
149−applyStaticDatabaseOptimization (RemoveInclusionDependency name) = return $ Right (RemoveInclusionDependency name)
150−
151−applyStaticDatabaseOptimization (AddNotification name triggerExpr resultExpr) = do
152− context <- get
153− let eTriggerExprOpt = evalState (applyStaticRelationalOptimization triggerExpr) (RelationalExprStateElems context)
154− case eTriggerExprOpt of
155− Left err -> pure $ Left err
156− Right triggerExprOpt -> do
157− let eResultExprOpt = evalState (applyStaticRelationalOptimization resultExpr) (RelationalExprStateElems context)
158− case eResultExprOpt of
159− Left err -> pure $ Left err
160− Right resultExprOpt -> pure (Right (AddNotification name triggerExprOpt resultExprOpt))
161−
162−applyStaticDatabaseOptimization notif@(RemoveNotification _) = pure (Right notif)
163−
164−applyStaticDatabaseOptimization c@(AddTypeConstructor _ _) = pure (Right c)
165−applyStaticDatabaseOptimization c@(RemoveTypeConstructor _) = pure (Right c)
166−applyStaticDatabaseOptimization c@(RemoveAtomFunction _) = pure (Right c)
167−
168−--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
169−
170−--applyStaticDatabaseOptimization (MultipleExpr exprs) = return $ Right $ MultipleExpr exprs
171−--for multiple expressions, we must evaluate
172−applyStaticDatabaseOptimization (MultipleExpr exprs) = do
173− context <- get
174− let optExprs = evalState substateRunner (contextWithEmptyTupleSets context)
175− let errors = lefts optExprs
176− if length errors > 0 then
177− return $ Left (head errors)
178− else
179− return $ Right $ MultipleExpr (rights optExprs)
180− where
181− substateRunner = forM exprs $ \expr -> do
182− --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
183− _ <- evalContextExpr expr
184− applyStaticDatabaseOptimization expr
185− --this error handling could be improved with some lifting presumably
186− --restore original context
187−
188−applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
189−applyStaticPredicateOptimization predicate = return $ Right predicate
190−
191−-- | Potentially optimize away constraints which need not be checked- they are tautologically valid because the update cannot possibly violate the constraint.
192−data Validation = NoValidationNeeded |
193− ValidationNeeded |
194− Violated --sometimes, we statically know that the inclusion dependency will be violated
195− deriving (Eq, Show, Ord)
196−
197−filterInclusionDependenciesForValidation :: DatabaseContextExpr -> InclusionDependencies -> Either RelationalError InclusionDependencies
198−filterInclusionDependenciesForValidation context incDeps = if M.size errors > 0 then
199− Left (MultipleErrors errors)
200− else
201− Right needValidation
202− where
203− violated = M.filter ((==) Violated) validationMap
204− errors = map InclusionDependencyCheckError (M.elems violated)
205− validationMap = M.map (inclusionDependencyValidation context) incDeps
206− needValidation = M.filter ((==) ValidationNeeded) validationMap
207−
208−
209−inclusionDependencyValidation :: DatabaseContextExpr -> InclusionDependency -> Validation
210−
211−inclusionDependencyValidation NoOperation _ = NoValidationNeeded
212−
213−-- a new relvar cannot possibly part of any constraint
214−inclusionDependencyValidation (Define _ _) _ = NoValidationNeeded
215−
216−-- if there are any foreign keys pointing to the relvar, then
217−inclusionDependencyValidation (Undefine name) incDep = if
218− _nameInIncDep name incDep then
219− Violated
220− else
221− NoValidationNeeded
222−
223−inclusionDependencyValidation (Assign name _) incDep = _nameInIncDepValidation name incDep
224−
225−inclusionDependencyValidation (Insert name _) incDep = _nameInIncDepValidation name incDep
226−
227−inclusionDependencyValidation (Delete name _) incDep = _nameInIncDepValidation name incDep
228−
229−inclusionDependencyValidation (Update name _ _) incDep = _nameInIncDepValidation name incDep
230−
231−--inc deps can't violate another inc dep- there is no change in relvars
232−inclusionDependencyValidation (AddInclusionDependency _ _) _ = NoValidationNeeded
233−
234−inclusionDependencyValidation (RemoveInclusionDependency _) _ = NoValidationNeeded
235−
236−inclusionDependencyValidation (AddNotification _ _ _) _ = NoValidationNeeded
237−
238−inclusionDependencyValidation (RemoveNotification _) _ = NoValidationNeeded
239−
240−inclusionDependencyValidation (AddTypeConstructor _ _) _ = NoValidationNeeded
241−
242−inclusionDependencyValidation (RemoveTypeConstructor _) _ = NoValidationNeeded
243−
244−inclusionDependencyValidation (RemoveAtomFunction _) _ = NoValidationNeeded
245−
246−inclusionDependencyValidation (MultipleExpr exprs) incDep = checkValidation validationSet
247− where
248− validationSet = S.fromList (map (flip inclusionDependencyValidation incDep) exprs)
249−
250−_nameInIncDep :: RelVarName -> InclusionDependency -> Bool
251−_nameInIncDep name incDep = S.member name (relvarReferences incDep)
252−
253−_nameInIncDepValidation :: RelVarName -> InclusionDependency -> Validation
254−_nameInIncDepValidation name incDep = if _nameInIncDep name incDep then
255− ValidationNeeded
256− else
257− NoValidationNeeded
258−
259−-- | Return what validation is needed after looking at optimizations over multiple expressions.
260−checkValidation :: S.Set Validation -> Validation
261−checkValidation vSet = if S.member Violated vSet then
262− Violated
263− else if S.member ValidationNeeded vSet || S.null vSet then
264− ValidationNeeded
265− else
266− NoValidationNeeded
267−
268−
src/lib/ProjectM36/StaticOptimizer/DatabaseContextExpression.hsnew file+86
@@ -0,0 +1,86 @@
1+module ProjectM36.StaticOptimizer.DatabaseContextExpression where
2+import ProjectM36.Base
3+import ProjectM36.Error
4+import ProjectM36.DatabaseContext
5+import ProjectM36.DatabaseContextExpression
6+import ProjectM36.RelationalExpressionState
7+import ProjectM36.StaticOptimizer.RelationalExpression
8+import Control.Monad.State
9+import Data.Either
10+
11+applyStaticDatabaseOptimization :: DatabaseContextExpr -> DatabaseState (Either RelationalError DatabaseContextExpr)
12+applyStaticDatabaseOptimization x@NoOperation = pure $ Right x
13+applyStaticDatabaseOptimization x@(Define _ _) = pure $ Right x
14+
15+applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
16+
17+applyStaticDatabaseOptimization (Assign name expr) = do
18+ context <- getDatabaseContext
19+ let optimizedExpr = evalState (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
20+ case optimizedExpr of
21+ Left err -> return $ Left err
22+ Right optimizedExpr2 -> return $ Right (Assign name optimizedExpr2)
23+
24+applyStaticDatabaseOptimization (Insert name expr) = do
25+ context <- getDatabaseContext
26+ let optimizedExpr = evalState (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
27+ case optimizedExpr of
28+ Left err -> return $ Left err
29+ Right optimizedExpr2 -> return $ Right (Insert name optimizedExpr2)
30+
31+applyStaticDatabaseOptimization (Delete name predicate) = do
32+ context <- getDatabaseContext
33+ let optimizedPredicate = evalState (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
34+ case optimizedPredicate of
35+ Left err -> return $ Left err
36+ Right optimizedPredicate2 -> return $ Right (Delete name optimizedPredicate2)
37+
38+applyStaticDatabaseOptimization (Update name upmap predicate) = do
39+ context <- getDatabaseContext
40+ let optimizedPredicate = evalState (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
41+ case optimizedPredicate of
42+ Left err -> return $ Left err
43+ Right optimizedPredicate2 -> return $ Right (Update name upmap optimizedPredicate2)
44+
45+applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = return $ Right dep
46+
47+applyStaticDatabaseOptimization (RemoveInclusionDependency name) = return $ Right (RemoveInclusionDependency name)
48+
49+applyStaticDatabaseOptimization (AddNotification name triggerExpr resultExpr) = do
50+ context <- getDatabaseContext
51+ let eTriggerExprOpt = evalState (applyStaticRelationalOptimization triggerExpr) (RelationalExprStateElems context)
52+ case eTriggerExprOpt of
53+ Left err -> pure $ Left err
54+ Right triggerExprOpt -> do
55+ let eResultExprOpt = evalState (applyStaticRelationalOptimization resultExpr) (RelationalExprStateElems context)
56+ case eResultExprOpt of
57+ Left err -> pure $ Left err
58+ Right resultExprOpt -> pure (Right (AddNotification name triggerExprOpt resultExprOpt))
59+
60+applyStaticDatabaseOptimization notif@(RemoveNotification _) = pure (Right notif)
61+
62+applyStaticDatabaseOptimization c@(AddTypeConstructor _ _) = pure (Right c)
63+applyStaticDatabaseOptimization c@(RemoveTypeConstructor _) = pure (Right c)
64+applyStaticDatabaseOptimization c@(RemoveAtomFunction _) = pure (Right c)
65+
66+--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
67+
68+--applyStaticDatabaseOptimization (MultipleExpr exprs) = return $ Right $ MultipleExpr exprs
69+--for multiple expressions, we must evaluate
70+applyStaticDatabaseOptimization (MultipleExpr exprs) = do
71+ dbstate <- get
72+ context <- getDatabaseContext
73+ let optExprs = evalState substateRunner (dbstate { dbcontext = contextWithEmptyTupleSets context })
74+ let errors = lefts optExprs
75+ if length errors > 0 then
76+ return $ Left (head errors)
77+ else
78+ return $ Right $ MultipleExpr (rights optExprs)
79+ where
80+ substateRunner = forM exprs $ \expr -> do
81+ --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
82+ _ <- evalContextExpr expr
83+ applyStaticDatabaseOptimization expr
84+ --this error handling could be improved with some lifting presumably
85+ --restore original context
86+
src/lib/ProjectM36/StaticOptimizer/InclusionDependencies.hsnew file+84
@@ -0,0 +1,84 @@
1+module ProjectM36.StaticOptimizer.InclusionDependencies where
2+import ProjectM36.Base
3+import ProjectM36.Error
4+import ProjectM36.RelationalExpression
5+import qualified Data.Map as M
6+import qualified Data.Set as S
7+
8+inclusionDependencyValidation :: DatabaseContextExpr -> InclusionDependency -> Validation
9+
10+inclusionDependencyValidation NoOperation _ = NoValidationNeeded
11+
12+-- a new relvar cannot possibly part of any constraint
13+inclusionDependencyValidation (Define _ _) _ = NoValidationNeeded
14+
15+-- if there are any foreign keys pointing to the relvar, then
16+inclusionDependencyValidation (Undefine name) incDep = if
17+ _nameInIncDep name incDep then
18+ Violated
19+ else
20+ NoValidationNeeded
21+
22+inclusionDependencyValidation (Assign name _) incDep = _nameInIncDepValidation name incDep
23+
24+inclusionDependencyValidation (Insert name _) incDep = _nameInIncDepValidation name incDep
25+
26+inclusionDependencyValidation (Delete name _) incDep = _nameInIncDepValidation name incDep
27+
28+inclusionDependencyValidation (Update name _ _) incDep = _nameInIncDepValidation name incDep
29+
30+--inc deps can't violate another inc dep- there is no change in relvars
31+inclusionDependencyValidation (AddInclusionDependency _ _) _ = NoValidationNeeded
32+
33+inclusionDependencyValidation (RemoveInclusionDependency _) _ = NoValidationNeeded
34+
35+inclusionDependencyValidation (AddNotification _ _ _) _ = NoValidationNeeded
36+
37+inclusionDependencyValidation (RemoveNotification _) _ = NoValidationNeeded
38+
39+inclusionDependencyValidation (AddTypeConstructor _ _) _ = NoValidationNeeded
40+
41+inclusionDependencyValidation (RemoveTypeConstructor _) _ = NoValidationNeeded
42+
43+inclusionDependencyValidation (RemoveAtomFunction _) _ = NoValidationNeeded
44+
45+inclusionDependencyValidation (MultipleExpr exprs) incDep = checkValidation validationSet
46+ where
47+ validationSet = S.fromList (map (flip inclusionDependencyValidation incDep) exprs)
48+
49+_nameInIncDep :: RelVarName -> InclusionDependency -> Bool
50+_nameInIncDep name incDep = S.member name (relvarReferences incDep)
51+
52+_nameInIncDepValidation :: RelVarName -> InclusionDependency -> Validation
53+_nameInIncDepValidation name incDep = if _nameInIncDep name incDep then
54+ ValidationNeeded
55+ else
56+ NoValidationNeeded
57+
58+-- | Return what validation is needed after looking at optimizations over multiple expressions.
59+checkValidation :: S.Set Validation -> Validation
60+checkValidation vSet = if S.member Violated vSet then
61+ Violated
62+ else if S.member ValidationNeeded vSet || S.null vSet then
63+ ValidationNeeded
64+ else
65+ NoValidationNeeded
66+
67+-- | Potentially optimize away constraints which need not be checked- they are tautologically valid because the update cannot possibly violate the constraint.
68+data Validation = NoValidationNeeded |
69+ ValidationNeeded |
70+ Violated --sometimes, we statically know that the inclusion dependency will be violated
71+ deriving (Eq, Show, Ord)
72+
73+filterInclusionDependenciesForValidation :: DatabaseContextExpr -> InclusionDependencies -> Either RelationalError InclusionDependencies
74+filterInclusionDependenciesForValidation context incDeps = if length errors > 0 then
75+ Left (MultipleErrors errors)
76+ else
77+ Right filteredIncDeps
78+ where
79+ filteredIncDeps = M.filterWithKey (\incDepName _ -> M.member incDepName needValidation) incDeps
80+ violated = M.filter ((==) Violated) validationMap
81+ errors = map InclusionDependencyCheckError (M.keys violated)
82+ validationMap = M.map (inclusionDependencyValidation context) incDeps
83+ needValidation = M.filter ((==) ValidationNeeded) validationMap
84+
src/lib/ProjectM36/StaticOptimizer/RelationalExpression.hsnew file+111
@@ -0,0 +1,111 @@
1+module ProjectM36.StaticOptimizer.RelationalExpression where
2+import ProjectM36.Base
3+import ProjectM36.RelationalExpressionState
4+import ProjectM36.Relation
5+import ProjectM36.RelationalExpression
6+import ProjectM36.Error
7+import qualified ProjectM36.AttributeNames as AS
8+import ProjectM36.TupleSet
9+
10+-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
11+-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
12+
13+--should optimizations offer the possibility to return errors? If they perform the up-front type-checking, maybe so
14+applyStaticRelationalOptimization :: RelationalExpr -> RelationalExprState (Either RelationalError RelationalExpr)
15+applyStaticRelationalOptimization e@(MakeStaticRelation _ _) = return $ Right e
16+
17+applyStaticRelationalOptimization e@(MakeRelationFromExprs _ _) = return $ Right e
18+
19+applyStaticRelationalOptimization e@(ExistingRelation _) = return $ Right e
20+
21+applyStaticRelationalOptimization e@(RelationVariable _ _) = return $ Right e
22+
23+--remove project of attributes which removes no attributes
24+applyStaticRelationalOptimization (Project attrNameSet expr) = do
25+ relType <- typeForRelationalExpr expr
26+ case relType of
27+ Left err -> return $ Left err
28+ Right relType2 -> if AS.all == attrNameSet then
29+ applyStaticRelationalOptimization expr
30+ else if AttributeNames (attributeNames relType2) == attrNameSet then
31+ applyStaticRelationalOptimization expr
32+ else do
33+ optimizedSubExpression <- applyStaticRelationalOptimization expr
34+ case optimizedSubExpression of
35+ Left err -> return $ Left err
36+ Right optSubExpr -> return $ Right $ Project attrNameSet optSubExpr
37+
38+applyStaticRelationalOptimization (Union exprA exprB) = do
39+ optExprA <- applyStaticRelationalOptimization exprA
40+ optExprB <- applyStaticRelationalOptimization exprB
41+ case optExprA of
42+ Left err -> return $ Left err
43+ Right optExprAx -> case optExprB of
44+ Left err -> return $ Left err
45+ Right optExprBx -> if optExprAx == optExprBx then
46+ return (Right optExprAx)
47+ else
48+ return $ Right $ Union optExprAx optExprBx
49+
50+applyStaticRelationalOptimization (Join exprA exprB) = do
51+ optExprA <- applyStaticRelationalOptimization exprA
52+ optExprB <- applyStaticRelationalOptimization exprB
53+ case optExprA of
54+ Left err -> return $ Left err
55+ Right optExprA2 -> case optExprB of
56+ Left err -> return $ Left err
57+ Right optExprB2 -> if optExprA == optExprB then --A join A == A
58+ return optExprA
59+ else
60+ return $ Right (Join optExprA2 optExprB2)
61+
62+applyStaticRelationalOptimization (Difference exprA exprB) = do
63+ optExprA <- applyStaticRelationalOptimization exprA
64+ optExprB <- applyStaticRelationalOptimization exprB
65+ case optExprA of
66+ Left err -> return $ Left err
67+ Right optExprA2 -> case optExprB of
68+ Left err -> return $ Left err
69+ Right optExprB2 -> if optExprA == optExprB then do --A difference A == A where false
70+ eEmptyRel <- typeForRelationalExpr optExprA2
71+ case eEmptyRel of
72+ Left err -> pure (Left err)
73+ Right emptyRel -> pure (Right (ExistingRelation emptyRel))
74+ else
75+ return $ Right (Difference optExprA2 optExprB2)
76+
77+applyStaticRelationalOptimization e@(Rename _ _ _) = return $ Right e
78+
79+applyStaticRelationalOptimization (Group oldAttrNames newAttrName expr) = do
80+ return $ Right $ Group oldAttrNames newAttrName expr
81+
82+applyStaticRelationalOptimization (Ungroup attrName expr) = do
83+ return $ Right $ Ungroup attrName expr
84+
85+--remove restriction of nothing
86+applyStaticRelationalOptimization (Restrict predicate expr) = do
87+ optimizedPredicate <- applyStaticPredicateOptimization predicate
88+ case optimizedPredicate of
89+ Left err -> return $ Left err
90+ Right optimizedPredicate2 -> if optimizedPredicate2 == TruePredicate then
91+ applyStaticRelationalOptimization expr
92+ else if optimizedPredicate2 == NotPredicate TruePredicate then do
93+ attributesRel <- typeForRelationalExpr expr
94+ case attributesRel of
95+ Left err -> return $ Left err
96+ Right attributesRelA -> return $ Right $ MakeStaticRelation (attributes attributesRelA) emptyTupleSet
97+ else do
98+ optimizedSubExpression <- applyStaticRelationalOptimization expr
99+ case optimizedSubExpression of
100+ Left err -> return $ Left err
101+ Right optSubExpr -> return $ Right $ Restrict optimizedPredicate2 optSubExpr
102+
103+applyStaticRelationalOptimization e@(Equals _ _) = return $ Right e
104+
105+applyStaticRelationalOptimization e@(NotEquals _ _) = return $ Right e
106+
107+applyStaticRelationalOptimization e@(Extend _ _) = return $ Right e
108+
109+
110+applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
111+applyStaticPredicateOptimization predicate = return $ Right predicate
src/lib/ProjectM36/TransGraphRelationalExpression.hs+1
@@ -6,6 +6,7 @@ import ProjectM36.Base
66import ProjectM36.TransactionGraph
77import ProjectM36.Transaction
88import ProjectM36.RelationalExpression
9+import ProjectM36.RelationalExpressionState
910import ProjectM36.Error
1011import Control.Monad.State
1112import ProjectM36.Tuple