on 016c152 help sign in
22 files changed+727−451
since splitsnapshots
unifiedsplit
project-m36.cabal+6−1 src/bin/TutorialD/Interpreter/DatabaseContextExpr.hs+17−10 src/bin/benchmark/bigrel.hs+1−1 src/lib/ProjectM36/Base.hs+2−1 src/lib/ProjectM36/Client.hs+31−20 src/lib/ProjectM36/DatabaseContext.hs+8 src/lib/ProjectM36/DatabaseContextExpression.hs+282 src/lib/ProjectM36/InclusionDependency.hs−1 src/lib/ProjectM36/InclusionDependencyValidation.hs+31 src/lib/ProjectM36/IsomorphicSchema.hs+20−15 src/lib/ProjectM36/Notifications.hs+1 src/lib/ProjectM36/RelationalExpression.hs+56−308 src/lib/ProjectM36/RelationalExpressionState.hs+42 src/lib/ProjectM36/StaticOptimizer/DatabaseContextExpression.hs+86 src/lib/ProjectM36/StaticOptimizer/InclusionDependencies.hs+84 src/lib/ProjectM36/StaticOptimizer.hs → src/lib/ProjectM36/StaticOptimizer/RelationalExpression.hs+5−80 src/lib/ProjectM36/TransGraphRelationalExpression.hs+1 test/IsomorphicSchema.hs+13−3 test/Relation/Basic.hs+1 test/Relation/StaticOptimizer.hs+26−4 test/TransactionGraph/Merge.hs+13−6 test/TutorialD/Interpreter.hs+1−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+evalDatabaseContextExprWithOpt :: Bool -> DatabaseContextExpr -> DatabaseState (Maybe RelationalError)
186+evalDatabaseContextExprWithOpt useOptimizer expr = do
187+ eOptimizedExpr <- applyStaticDatabaseOptimization expr
188+ case eOptimizedExpr of
189+ Left err -> pure (Just err)
190+ Right optimizedExpr -> evalDatabaseContextExpr (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 (evalDatabaseContextExprWithOpt 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/bin/benchmark/bigrel.hs+1−1
@@ -59,7 +59,7 @@ matrixRun (BigrelArgs attributeCount tupleCount tutd) = do
5959 putStrLn "Done."
6060 else do
6161 let setx = Assign "x" (ExistingRelation (force rel))
62− context = execState (evalContextExpr setx) dateExamples
62+ context = execState (evalDatabaseContextExpr setx) dateExamples
6363 interpreted = interpretDatabaseContextExpr context tutd
6464 --plan = interpretRODatabaseContextOp context $ ":showplan " ++ tutd
6565 --displayOpResult plan
src/lib/ProjectM36/Base.hs+2−1
@@ -1,4 +1,4 @@
1−{-# LANGUAGE ExistentialQuantification,BangPatterns,DeriveGeneric,DeriveAnyClass, TypeSynonymInstances, FlexibleInstances #-}
1+{-# LANGUAGE ExistentialQuantification,BangPatterns,DeriveGeneric,DeriveAnyClass, TypeSynonymInstances, FlexibleInstances, DeriveFoldable #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33module ProjectM36.Base where
44import qualified Data.Map as M
@@ -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.evalDatabaseContextExpr 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+282
@@ -0,0 +1,282 @@
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+evalDatabaseContextExpr :: DatabaseContextExpr -> DatabaseState (Maybe RelationalError)
84+evalDatabaseContextExpr NoOperation = pure Nothing
85+
86+evalDatabaseContextExpr 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+evalDatabaseContextExpr expr@(Undefine relVarName) = do
100+ deleteRelVar expr relVarName
101+
102+evalDatabaseContextExpr 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+evalDatabaseContextExpr (Insert relVarName relExpr) = evalDatabaseContextExpr $ Assign relVarName (Union relExpr (RelationVariable relVarName ()))
120+
121+evalDatabaseContextExpr 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+evalDatabaseContextExpr 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+evalDatabaseContextExpr 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+ --when adding a constraint, it is new and must be unconditionally validated without being optimized away
157+ validateConstraints expr potentialContext
158+
159+evalDatabaseContextExpr (RemoveInclusionDependency depName) = do
160+ currContext <- getDatabaseContext
161+ let currDeps = inclusionDependencies currContext
162+ newDeps = M.delete depName currDeps
163+ if M.notMember depName currDeps then
164+ return $ Just (InclusionDependencyNameNotInUseError depName)
165+ else do
166+ putDatabaseContext $ currContext {inclusionDependencies = newDeps }
167+ return Nothing
168+
169+-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
170+evalDatabaseContextExpr (AddNotification notName triggerExpr resultExpr) = do
171+ currentContext <- getDatabaseContext
172+ let nots = notifications currentContext
173+ if M.member notName nots then
174+ return $ Just (NotificationNameInUseError notName)
175+ else do
176+ let newNotifications = M.insert notName newNotification nots
177+ newNotification = Notification { changeExpr = triggerExpr,
178+ reportExpr = resultExpr }
179+ putDatabaseContext $ currentContext { notifications = newNotifications }
180+ return Nothing
181+
182+evalDatabaseContextExpr (RemoveNotification notName) = do
183+ currentContext <- getDatabaseContext
184+ let nots = notifications currentContext
185+ if M.notMember notName nots then
186+ return $ Just (NotificationNameNotInUseError notName)
187+ else do
188+ let newNotifications = M.delete notName nots
189+ putDatabaseContext $ currentContext { notifications = newNotifications }
190+ return Nothing
191+
192+-- | Adds type and data constructors to the database context.
193+-- validate that the type *and* constructor names are unique! not yet implemented!
194+evalDatabaseContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
195+ currentContext <- getDatabaseContext
196+ let oldTypes = typeConstructorMapping currentContext
197+ tConsName = TCD.name tConsDef
198+ -- validate that the constructor's types exist
199+ case validateTypeConstructorDef tConsDef dConsDefList of
200+ errs@(_:_) -> pure $ Just (someErrors errs)
201+ [] -> do
202+ if T.length tConsName < 1 || not (isUpper (T.head tConsName)) then
203+ pure $ Just (InvalidAtomTypeName tConsName)
204+ else if isJust (findTypeConstructor tConsName oldTypes) then
205+ pure $ Just (AtomTypeNameInUseError tConsName)
206+ else do
207+ let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
208+ putDatabaseContext $ currentContext { typeConstructorMapping = newTypes }
209+ pure Nothing
210+
211+-- | 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.
212+evalDatabaseContextExpr (RemoveTypeConstructor tConsName) = do
213+ currentContext <- getDatabaseContext
214+ let oldTypes = typeConstructorMapping currentContext
215+ if findTypeConstructor tConsName oldTypes == Nothing then
216+ pure $ Just (AtomTypeNameNotInUseError tConsName)
217+ else do
218+ let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
219+ putDatabaseContext $ currentContext { typeConstructorMapping = newTypes }
220+ pure Nothing
221+
222+evalDatabaseContextExpr (MultipleExpr exprs) = do
223+ --the multiple expressions must pass the same context around- not the old unmodified context
224+ evald <- forM exprs evalDatabaseContextExpr
225+ --some lifting magic needed here
226+ case catMaybes evald of
227+ [] -> return $ Nothing
228+ err:_ -> return $ Just err
229+
230+evalDatabaseContextExpr (RemoveAtomFunction funcName) = do
231+ currentContext <- getDatabaseContext
232+ let atomFuncs = atomFunctions currentContext
233+ dudFunc = emptyAtomFunction funcName -- just for lookup in the hashset
234+ if HS.member dudFunc atomFuncs then do
235+ let updatedFuncs = HS.delete dudFunc atomFuncs
236+ putDatabaseContext (currentContext {atomFunctions = updatedFuncs })
237+ pure Nothing
238+ else
239+ pure (Just (AtomFunctionNameNotInUseError funcName))
240+
241+evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
242+evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) = do
243+ case mScriptSession of
244+ Nothing -> pure (Left (AtomFunctionBodyScriptError ScriptCompilationDisabledError))
245+ Just scriptSession -> do
246+ res <- try $ runGhc (Just libdir) $ do
247+ setSession (hscEnv scriptSession)
248+ let atomFuncs = atomFunctions currentContext
249+ --compile the function
250+ eCompiledFunc <- compileAtomFunctionScript scriptSession script
251+ pure $ case eCompiledFunc of
252+ Left err -> Left (AtomFunctionBodyScriptError err)
253+ Right compiledFunc -> do
254+ funcAtomType <- mapM (\funcTypeArg -> atomTypeForTypeConstructor funcTypeArg (typeConstructorMapping currentContext)) funcType
255+ let updatedFuncs = HS.insert newAtomFunc atomFuncs
256+ newContext = currentContext { atomFunctions = updatedFuncs }
257+ newAtomFunc = AtomFunction { atomFuncName = funcName,
258+ atomFuncType = funcAtomType,
259+ atomFuncBody = AtomFunctionBody (Just script) compiledFunc }
260+
261+ -- check if the name is already in use
262+ if HS.member funcName (HS.map atomFuncName atomFuncs) then
263+ Left (AtomFunctionNameInUseError funcName)
264+ else do
265+ Right newContext
266+ case res of
267+ Left (exc :: SomeException) -> pure $ Left (AtomFunctionBodyScriptError (OtherScriptCompilationError (show exc)))
268+ Right eContext -> case eContext of
269+ Left err -> pure (Left err)
270+ Right context' -> pure (Right context')
271+
272+updateTupleWithAtomExprs :: (M.Map AttributeName AtomExpr) -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
273+updateTupleWithAtomExprs exprMap context tupIn = do
274+ --resolve all atom exprs
275+ atomsAssoc <- mapM (\(attrName, atomExpr) -> do
276+ atom <- evalState (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
277+ pure (attrName, atom)
278+ ) (M.toList exprMap)
279+ pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
280+
281+-- 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
282+-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
src/lib/ProjectM36/InclusionDependency.hs−1
@@ -17,4 +17,3 @@ inclusionDependenciesAsRelation incDeps = do
1717 incDepAsAtoms (name, (InclusionDependency exprA exprB)) = [TextAtom name,
1818 TextAtom (T.pack (show exprA)),
1919 TextAtom (T.pack (show exprB))]
20−
src/lib/ProjectM36/InclusionDependencyValidation.hsnew file+31
@@ -0,0 +1,31 @@
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+ err:[] -> Just err
18+ list -> Just (MultipleErrors list)
19+ where
20+ optimizedIncDepMap = filterInclusionDependenciesForValidation expr deps
21+ failures incDeps = M.elems $ M.mapMaybeWithKey checkIncDep incDeps
22+ deps = inclusionDependencies context
23+ eval expr' = evalState (evalRelationalExpr expr') (RelationalExprStateElems context)
24+ checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
25+ let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
26+ case eval checkExpr of
27+ Left err -> Just err
28+ Right resultRel -> if resultRel == relationTrue then
29+ Nothing
30+ else
31+ 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 <- evalDatabaseContextExpr 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+56−308
@@ -2,67 +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
10+import ProjectM36.RelationalExpressionState
11+import ProjectM36.DatabaseContext
1212import qualified ProjectM36.Attribute as A
1313import qualified Data.Map as M
14−import qualified Data.HashSet as HS
14+import qualified Data.Set as S
1515import Control.Monad.State hiding (join)
16−import Control.Exception
1716import Data.Maybe
1817import Data.Either
19−import Data.Char (isUpper)
20−import qualified Data.Text as T
18+
2119import qualified Data.Vector as V
22−import qualified ProjectM36.TypeConstructorDef as TCD
2320import Control.Monad.Trans.Except
2421
25−import GHC
26−import GHC.Paths
27−
28−-- we need to pass around a higher level RelationTuple and Attributes in order to solve #52
29−data RelationalExprStateElems = RelationalExprStateTupleElems DatabaseContext RelationTuple | -- used when fully evaluating a relexpr
30− RelationalExprStateAttrsElems DatabaseContext Attributes | --used when evaluating the type of a relexpr
31− RelationalExprStateElems DatabaseContext --used by default at the top level of evaluation
32−
33−instance Show RelationalExprStateElems where
34− show (RelationalExprStateTupleElems _ tup) = "RelationalExprStateTupleElems " ++ show tup
35− show (RelationalExprStateAttrsElems _ attrs) = "RelationalExprStateAttrsElems" ++ show attrs
36− show (RelationalExprStateElems _) = "RelationalExprStateElems"
37−
38−mkRelationalExprState :: DatabaseContext -> RelationalExprStateElems
39−mkRelationalExprState ctx = RelationalExprStateElems ctx
40−
41−mergeTuplesIntoRelationalExprState :: RelationTuple -> RelationalExprStateElems -> RelationalExprStateElems
42−mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateElems ctx) = RelationalExprStateTupleElems ctx tupIn
43−mergeTuplesIntoRelationalExprState _ st@(RelationalExprStateAttrsElems _ _) = st
44−mergeTuplesIntoRelationalExprState tupIn (RelationalExprStateTupleElems ctx existingTuple) = let mergedTupMap = M.union (tupleToMap tupIn) (tupleToMap existingTuple) in
45− RelationalExprStateTupleElems ctx (mkRelationTupleFromMap mergedTupMap)
46−
47−mergeAttributesIntoRelationalExprState :: Attributes -> RelationalExprStateElems -> RelationalExprStateElems
48−mergeAttributesIntoRelationalExprState attrs (RelationalExprStateElems ctx) = RelationalExprStateAttrsElems ctx attrs
49−mergeAttributesIntoRelationalExprState _ st@(RelationalExprStateTupleElems _ _) = st
50−mergeAttributesIntoRelationalExprState attrsIn (RelationalExprStateAttrsElems ctx attrs) = RelationalExprStateAttrsElems ctx (A.union attrsIn attrs)
51−
52−type DatabaseState a = State DatabaseContext a
53−
54−type RelationalExprState a = State RelationalExprStateElems a
55−
56−stateContext :: RelationalExprStateElems -> DatabaseContext
57−stateContext (RelationalExprStateTupleElems ctx _) = ctx
58−stateContext (RelationalExprStateElems ctx) = ctx
59−stateContext (RelationalExprStateAttrsElems ctx _) = ctx
60−
61−setStateContext :: RelationalExprStateElems -> DatabaseContext -> RelationalExprStateElems
62−setStateContext (RelationalExprStateTupleElems _ tup) ctx = RelationalExprStateTupleElems ctx tup
63−setStateContext (RelationalExprStateElems _) ctx = RelationalExprStateElems ctx
64−setStateContext (RelationalExprStateAttrsElems _ attrs) ctx = RelationalExprStateAttrsElems ctx attrs
65−
6622--relvar state is needed in evaluation of relational expression but only as read-only in order to extract current relvar values
6723evalRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
6824evalRelationalExpr (RelationVariable name _) = do
@@ -185,259 +141,6 @@ evalRelationalExpr (Extend tupleExpression relExpr) = do
185141 Left err -> pure (Left err)
186142 Right (newAttrs, tupProc') -> pure $ relMogrify tupProc' newAttrs rel
187143
188−emptyDatabaseContext :: DatabaseContext
189−emptyDatabaseContext = DatabaseContext { inclusionDependencies = M.empty,
190− relationVariables = M.empty,
191− atomFunctions = HS.empty,
192− notifications = M.empty,
193− typeConstructorMapping = []
194− }
195−
196−
197−
198−--helper function to process relation variable creation/assignment
199−setRelVar :: RelVarName -> Relation -> DatabaseState (Maybe RelationalError)
200−setRelVar relVarName rel = do
201− currentContext <- get
202− let newRelVars = M.insert relVarName rel $ relationVariables currentContext
203− potentialContext = currentContext { relationVariables = newRelVars }
204−
205− case checkConstraints potentialContext of
206− Just err -> return $ Just err
207− Nothing -> do
208− put potentialContext
209− return Nothing
210−
211−-- 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
212−deleteRelVar :: RelVarName -> DatabaseState (Maybe RelationalError)
213−deleteRelVar relVarName = do
214− currContext <- get
215− let newRelVars = M.delete relVarName (relationVariables currContext)
216− newContext = currContext { relationVariables = newRelVars }
217− put newContext
218− return Nothing
219−
220−evalContextExpr :: DatabaseContextExpr -> DatabaseState (Maybe RelationalError)
221−evalContextExpr NoOperation = pure Nothing
222−
223−evalContextExpr (Define relVarName attrExprs) = do
224− relvars <- liftM relationVariables get
225− tConss <- liftM typeConstructorMapping get
226− let eAttrs = map (evalAttrExpr tConss) attrExprs
227− case lefts eAttrs of
228− err:_ -> pure (Just err)
229− [] -> case M.member relVarName relvars of
230− True -> return (Just (RelVarAlreadyDefinedError relVarName))
231− False -> setRelVar relVarName emptyRelation
232− where
233− attrs = A.attributesFromList (rights eAttrs)
234− emptyRelation = Relation attrs emptyTupleSet
235−
236−evalContextExpr (Undefine relVarName) = do
237− deleteRelVar relVarName
238−
239−evalContextExpr (Assign relVarName expr) = do
240− -- in the future, it would be nice to get types from the RelationalExpr instead of needing to evaluate it
241− context <- get
242− let existingRelVar = M.lookup relVarName relVarTable
243− relVarTable = relationVariables context
244− value = evalState (evalRelationalExpr expr) (RelationalExprStateElems context)
245− case value of
246− Left err -> return $ Just err
247− Right rel -> case existingRelVar of
248− Nothing -> setRelVar relVarName rel
249− Just existingRel -> let expectedAttributes = attributes existingRel
250− foundAttributes = attributes rel in
251− if A.attributesEqual expectedAttributes foundAttributes then
252− setRelVar relVarName rel
253− else
254− return $ Just (RelVarAssignmentTypeMismatchError expectedAttributes foundAttributes)
255−
256−evalContextExpr (Insert relVarName relExpr) = evalContextExpr $ Assign relVarName (Union relExpr (RelationVariable relVarName ()))
257−
258−evalContextExpr (Delete relVarName predicate) = do
259− context <- get
260− let updatedRel = evalState (evalRelationalExpr (Restrict (NotPredicate predicate) (RelationVariable relVarName ()))) (RelationalExprStateElems context)
261− case updatedRel of
262− Left err -> return $ Just err
263− Right rel -> setRelVar relVarName rel
264−
265−--union of restricted+updated portion and the unrestricted+unupdated portion
266−evalContextExpr (Update relVarName atomExprMap restrictionPredicateExpr) = do
267− context <- get
268− let relVarTable = relationVariables context
269− case M.lookup relVarName relVarTable of
270− Nothing -> return $ Just (RelVarNotDefinedError relVarName)
271− Just rel -> do
272− case evalState (predicateRestrictionFilter (attributes rel) restrictionPredicateExpr) (RelationalExprStateElems context) of
273− Left err -> return $ Just err
274− Right predicateFunc -> do
275− case makeUpdatedRel rel of
276− Left err -> return $ Just err
277− Right updatedRel -> setRelVar relVarName updatedRel
278− where
279− makeUpdatedRel relin = do
280− restrictedPortion <- restrict predicateFunc relin
281− unrestrictedPortion <- restrict (not . predicateFunc) relin
282− updatedPortion <- relMap (updateTupleWithAtomExprs atomExprMap context) restrictedPortion
283− union updatedPortion unrestrictedPortion
284−
285−evalContextExpr (AddInclusionDependency newDepName newDep) = do
286− currContext <- get
287− let currDeps = inclusionDependencies currContext
288− newDeps = M.insert newDepName newDep currDeps
289− if M.member newDepName currDeps then
290− return $ Just (InclusionDependencyNameInUseError newDepName)
291− else do
292− let potentialContext = currContext { inclusionDependencies = newDeps }
293− case checkConstraints potentialContext of
294− Just err -> return $ Just err
295− Nothing -> do
296− put potentialContext
297− return Nothing
298−
299−evalContextExpr (RemoveInclusionDependency depName) = do
300− currContext <- get
301− let currDeps = inclusionDependencies currContext
302− newDeps = M.delete depName currDeps
303− if M.notMember depName currDeps then
304− return $ Just (InclusionDependencyNameNotInUseError depName)
305− else do
306− put $ currContext {inclusionDependencies = newDeps }
307− return Nothing
308−
309−-- | Add a notification which will send the resultExpr when triggerExpr changes between commits.
310−evalContextExpr (AddNotification notName triggerExpr resultExpr) = do
311− currentContext <- get
312− let nots = notifications currentContext
313− if M.member notName nots then
314− return $ Just (NotificationNameInUseError notName)
315− else do
316− let newNotifications = M.insert notName newNotification nots
317− newNotification = Notification { changeExpr = triggerExpr,
318− reportExpr = resultExpr }
319− put $ currentContext { notifications = newNotifications }
320− return Nothing
321−
322−evalContextExpr (RemoveNotification notName) = do
323− currentContext <- get
324− let nots = notifications currentContext
325− if M.notMember notName nots then
326− return $ Just (NotificationNameNotInUseError notName)
327− else do
328− let newNotifications = M.delete notName nots
329− put $ currentContext { notifications = newNotifications }
330− return Nothing
331−
332−-- | Adds type and data constructors to the database context.
333−-- validate that the type *and* constructor names are unique! not yet implemented!
334−evalContextExpr (AddTypeConstructor tConsDef dConsDefList) = do
335− currentContext <- get
336− let oldTypes = typeConstructorMapping currentContext
337− tConsName = TCD.name tConsDef
338− -- validate that the constructor's types exist
339− case validateTypeConstructorDef tConsDef dConsDefList of
340− errs@(_:_) -> pure $ Just (someErrors errs)
341− [] -> do
342− if T.length tConsName < 1 || not (isUpper (T.head tConsName)) then
343− pure $ Just (InvalidAtomTypeName tConsName)
344− else if isJust (findTypeConstructor tConsName oldTypes) then
345− pure $ Just (AtomTypeNameInUseError tConsName)
346− else do
347− let newTypes = oldTypes ++ [(tConsDef, dConsDefList)]
348− put $ currentContext { typeConstructorMapping = newTypes }
349− pure Nothing
350−
351−-- | 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.
352−evalContextExpr (RemoveTypeConstructor tConsName) = do
353− currentContext <- get
354− let oldTypes = typeConstructorMapping currentContext
355− if findTypeConstructor tConsName oldTypes == Nothing then
356− pure $ Just (AtomTypeNameNotInUseError tConsName)
357− else do
358− let newTypes = filter (\(tCons, _) -> TCD.name tCons /= tConsName) oldTypes
359− put $ currentContext { typeConstructorMapping = newTypes }
360− pure Nothing
361−
362−evalContextExpr (MultipleExpr exprs) = do
363− --the multiple expressions must pass the same context around- not the old unmodified context
364− evald <- forM exprs evalContextExpr
365− --some lifting magic needed here
366− case catMaybes evald of
367− [] -> return $ Nothing
368− err:_ -> return $ Just err
369−
370−evalContextExpr (RemoveAtomFunction funcName) = do
371− currentContext <- get
372− let atomFuncs = atomFunctions currentContext
373− dudFunc = emptyAtomFunction funcName -- just for lookup in the hashset
374− if HS.member dudFunc atomFuncs then do
375− let updatedFuncs = HS.delete dudFunc atomFuncs
376− put (currentContext {atomFunctions = updatedFuncs })
377− pure Nothing
378− else
379− pure (Just (AtomFunctionNameNotInUseError funcName))
380−
381−evalDatabaseContextIOExpr :: Maybe ScriptSession -> DatabaseContext -> DatabaseContextIOExpr -> IO (Either RelationalError DatabaseContext)
382−evalDatabaseContextIOExpr mScriptSession currentContext (AddAtomFunction funcName funcType script) = do
383− case mScriptSession of
384− Nothing -> pure (Left (AtomFunctionBodyScriptError ScriptCompilationDisabledError))
385− Just scriptSession -> do
386− res <- try $ runGhc (Just libdir) $ do
387− setSession (hscEnv scriptSession)
388− let atomFuncs = atomFunctions currentContext
389− --compile the function
390− eCompiledFunc <- compileAtomFunctionScript scriptSession script
391− pure $ case eCompiledFunc of
392− Left err -> Left (AtomFunctionBodyScriptError err)
393− Right compiledFunc -> do
394− funcAtomType <- mapM (\funcTypeArg -> atomTypeForTypeConstructor funcTypeArg (typeConstructorMapping currentContext)) funcType
395− let updatedFuncs = HS.insert newAtomFunc atomFuncs
396− newContext = currentContext { atomFunctions = updatedFuncs }
397− newAtomFunc = AtomFunction { atomFuncName = funcName,
398− atomFuncType = funcAtomType,
399− atomFuncBody = AtomFunctionBody (Just script) compiledFunc }
400−
401− -- check if the name is already in use
402− if HS.member funcName (HS.map atomFuncName atomFuncs) then
403− Left (AtomFunctionNameInUseError funcName)
404− else do
405− Right newContext
406− case res of
407− Left (exc :: SomeException) -> pure $ Left (AtomFunctionBodyScriptError (OtherScriptCompilationError (show exc)))
408− Right eContext -> case eContext of
409− Left err -> pure (Left err)
410− Right context' -> pure (Right context')
411−
412−updateTupleWithAtomExprs :: (M.Map AttributeName AtomExpr) -> DatabaseContext -> RelationTuple -> Either RelationalError RelationTuple
413−updateTupleWithAtomExprs exprMap context tupIn = do
414− --resolve all atom exprs
415− atomsAssoc <- mapM (\(attrName, atomExpr) -> do
416− atom <- evalState (evalAtomExpr tupIn atomExpr) (RelationalExprStateElems context)
417− pure (attrName, atom)
418− ) (M.toList exprMap)
419− pure (updateTupleWithAtoms (M.fromList atomsAssoc) tupIn)
420−
421−--run verification on all constraints
422−checkConstraints :: DatabaseContext -> Maybe RelationalError
423−checkConstraints context = case failures of
424− [] -> Nothing
425− l:_ -> Just l
426− where
427− failures = M.elems $ M.mapMaybeWithKey checkIncDep deps
428− deps = inclusionDependencies context
429− eval expr = evalState (evalRelationalExpr expr) (RelationalExprStateElems context)
430− checkIncDep depName (InclusionDependency subsetExpr supersetExpr) = do
431− let checkExpr = Equals supersetExpr (Union subsetExpr supersetExpr)
432− case eval checkExpr of
433− Left err -> Just err
434− Right resultRel -> if resultRel == relationTrue then
435− Nothing
436− else
437− Just $ InclusionDependencyCheckError depName
438−
439−-- 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
440−-- furthermore, the type of a relational expression is the resultant header of the evaluated empty-tupled relation
441144
442145typeForRelationalExpr :: RelationalExpr -> RelationalExprState (Either RelationalError Relation)
443146typeForRelationalExpr expr = do
@@ -448,13 +151,6 @@ typeForRelationalExpr expr = do
448151 rstate' = setStateContext rstate context'
449152 pure (evalState (evalRelationalExpr expr) rstate')
450153
451−--returns a database context with all tuples removed
452−--this is useful for type checking and optimization
453−contextWithEmptyTupleSets :: DatabaseContext -> DatabaseContext
454−contextWithEmptyTupleSets contextIn = contextIn { relationVariables = relVars }
455− where
456− relVars = M.map (\rel -> Relation (attributes rel) emptyTupleSet) (relationVariables contextIn)
457−
458154liftE :: (Monad m) => m (Either a b) -> ExceptT a m b
459155liftE v = do
460156 y <- lift v
@@ -688,3 +384,55 @@ evalTupleExpr attrs (TupleExpr tupMap) = runExceptT $ do
688384 _ <- either throwE pure (validateTuple tup' tConss)
689385 pure tup'
690386
387+ -- | Return all relation variable names mentioned in the relational expression. This is used by the constraint static optimizer to determine which relvars are relevant.
388+relationVariableNames :: Show a => RelationalExprBase a -> S.Set RelVarName
389+relationVariableNames relExpr = foldRelationalExprs (\names re -> case re of
390+ RelationVariable name _ -> S.insert name names
391+ _ -> names) S.empty relExpr
392+
393+-- | Return all the unit RelationalExprs. This is useful, for example, for finding all mentions of relation
394+foldRelationalExprs :: (b -> RelationalExprBase a -> b) -> b -> RelationalExprBase a -> b
395+foldRelationalExprs func accum expr = case expr of
396+ e@(MakeRelationFromExprs _ tupleExprs) -> foldl func accum (e : concatMap tupleExprRelationalExprs tupleExprs)
397+ e@(MakeStaticRelation _ _) -> folder e []
398+ e@(ExistingRelation _) -> folder e []
399+ e@(RelationVariable _ _) -> folder e []
400+ e@(Project _ expr2) -> folder e [expr2]
401+ e@(Union exprA exprB) -> folder e [exprA, exprB]
402+ e@(Join exprA exprB) -> folder e [exprA, exprB]
403+ e@(Rename _ _ expr2) -> folder e [expr2]
404+ e@(Difference exprA exprB) -> folder e [exprA, exprB]
405+ e@(Group _ _ expr2) -> folder e [expr2]
406+ e@(Ungroup _ expr2) -> folder e [expr2]
407+ e@(Restrict predi expr2) -> folder e (expr2 : restrictionPredicateRelationalExprs predi)
408+ e@(Equals exprA exprB) -> folder e [exprA, exprB]
409+ e@(NotEquals exprA exprB) -> folder e [exprA, exprB]
410+ e@(Extend extendExpr expr2) -> folder e (expr2 : extendTupleRelationalExprs extendExpr)
411+ where
412+ folder e exprs = func (foldl (foldRelationalExprs func) accum exprs) e
413+
414+-- | Scan tupleExpr to return RelationExprs.
415+tupleExprRelationalExprs :: TupleExprBase a -> [RelationalExprBase a]
416+tupleExprRelationalExprs (TupleExpr amap) = concat (map atomExprRelationalExprs (M.elems amap))
417+
418+atomExprRelationalExprs :: AtomExprBase a -> [RelationalExprBase a]
419+atomExprRelationalExprs (FunctionAtomExpr _ exprs _) = concatMap atomExprRelationalExprs exprs
420+atomExprRelationalExprs (RelationAtomExpr expr) = [expr]
421+atomExprRelationalExprs (ConstructedAtomExpr _ exprs _) = concatMap atomExprRelationalExprs exprs
422+atomExprRelationalExprs _ = []
423+
424+restrictionPredicateRelationalExprs :: RestrictionPredicateExprBase a -> [RelationalExprBase a]
425+restrictionPredicateRelationalExprs predi = case predi of
426+ TruePredicate -> []
427+ AndPredicate exprA exprB -> restrictionPredicateRelationalExprs exprA ++ restrictionPredicateRelationalExprs exprB
428+ OrPredicate exprA exprB -> restrictionPredicateRelationalExprs exprA ++ restrictionPredicateRelationalExprs exprB
429+ NotPredicate expr -> restrictionPredicateRelationalExprs expr
430+ RelationalExprPredicate relExpr -> foldRelationalExprs (\acc expr -> expr : acc) [] relExpr
431+ AtomExprPredicate atomExpr -> atomExprRelationalExprs atomExpr
432+ AttributeEqualityPredicate _ atomExpr -> atomExprRelationalExprs atomExpr
433+
434+extendTupleRelationalExprs :: ExtendTupleExprBase a -> [RelationalExprBase a]
435+extendTupleRelationalExprs (AttributeExtendTupleExpr _ atomExpr) = atomExprRelationalExprs atomExpr
436+
437+relvarReferences :: InclusionDependency -> S.Set RelVarName
438+relvarReferences (InclusionDependency super sub) = S.union (relationVariableNames super) (relationVariableNames sub)
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/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+ _ <- evalDatabaseContextExpr 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, but a new inc dep must be unconditionally validated
31+inclusionDependencyValidation (AddInclusionDependency _ _) _ = ValidationNeeded
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 expr 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 expr) incDeps
83+ needValidation = M.filter ((==) ValidationNeeded) validationMap
84+
src/lib/ProjectM36/StaticOptimizer.hs → src/lib/ProjectM36/StaticOptimizer/RelationalExpression.hsrenamed+5−80
@@ -1,12 +1,11 @@
1−module ProjectM36.StaticOptimizer where
1+module ProjectM36.StaticOptimizer.RelationalExpression where
22import ProjectM36.Base
3−import ProjectM36.RelationalExpression
3+import ProjectM36.RelationalExpressionState
44import ProjectM36.Relation
5+import ProjectM36.RelationalExpression
56import ProjectM36.Error
67import qualified ProjectM36.AttributeNames as AS
78import ProjectM36.TupleSet
8−import Control.Monad.State hiding (join)
9−import Data.Either (rights, lefts)
109
1110-- the static optimizer performs optimizations which need not take any specific-relation statistics into account
1211-- apply optimizations which merely remove steps to become no-ops: example: projection of a relation across all of its attributes => original relation
@@ -107,80 +106,6 @@ applyStaticRelationalOptimization e@(NotEquals _ _) = return $ Right e
107106
108107applyStaticRelationalOptimization e@(Extend _ _) = return $ Right e
109108
110−applyStaticDatabaseOptimization :: DatabaseContextExpr -> DatabaseState (Either RelationalError DatabaseContextExpr)
111−applyStaticDatabaseOptimization x@NoOperation = pure $ Right x
112−applyStaticDatabaseOptimization x@(Define _ _) = pure $ Right x
113−
114−applyStaticDatabaseOptimization x@(Undefine _) = pure $ Right x
115−
116−applyStaticDatabaseOptimization (Assign name expr) = do
117− context <- get
118− let optimizedExpr = evalState (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
119− case optimizedExpr of
120− Left err -> return $ Left err
121− Right optimizedExpr2 -> return $ Right (Assign name optimizedExpr2)
122−
123−applyStaticDatabaseOptimization (Insert name expr) = do
124− context <- get
125− let optimizedExpr = evalState (applyStaticRelationalOptimization expr) (RelationalExprStateElems context)
126− case optimizedExpr of
127− Left err -> return $ Left err
128− Right optimizedExpr2 -> return $ Right (Insert name optimizedExpr2)
129−
130−applyStaticDatabaseOptimization (Delete name predicate) = do
131− context <- get
132− let optimizedPredicate = evalState (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
133− case optimizedPredicate of
134− Left err -> return $ Left err
135− Right optimizedPredicate2 -> return $ Right (Delete name optimizedPredicate2)
136−
137−applyStaticDatabaseOptimization (Update name upmap predicate) = do
138− context <- get
139− let optimizedPredicate = evalState (applyStaticPredicateOptimization predicate) (RelationalExprStateElems context)
140− case optimizedPredicate of
141− Left err -> return $ Left err
142− Right optimizedPredicate2 -> return $ Right (Update name upmap optimizedPredicate2)
143−
144−applyStaticDatabaseOptimization dep@(AddInclusionDependency _ _) = return $ Right dep
145−
146−applyStaticDatabaseOptimization (RemoveInclusionDependency name) = return $ Right (RemoveInclusionDependency name)
147−
148−applyStaticDatabaseOptimization (AddNotification name triggerExpr resultExpr) = do
149− context <- get
150− let eTriggerExprOpt = evalState (applyStaticRelationalOptimization triggerExpr) (RelationalExprStateElems context)
151− case eTriggerExprOpt of
152− Left err -> pure $ Left err
153− Right triggerExprOpt -> do
154− let eResultExprOpt = evalState (applyStaticRelationalOptimization resultExpr) (RelationalExprStateElems context)
155− case eResultExprOpt of
156− Left err -> pure $ Left err
157− Right resultExprOpt -> pure (Right (AddNotification name triggerExprOpt resultExprOpt))
158−
159−applyStaticDatabaseOptimization notif@(RemoveNotification _) = pure (Right notif)
160−
161−applyStaticDatabaseOptimization c@(AddTypeConstructor _ _) = pure (Right c)
162−applyStaticDatabaseOptimization c@(RemoveTypeConstructor _) = pure (Right c)
163−applyStaticDatabaseOptimization c@(RemoveAtomFunction _) = pure (Right c)
164−
165−--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
166−
167−--applyStaticDatabaseOptimization (MultipleExpr exprs) = return $ Right $ MultipleExpr exprs
168−--for multiple expressions, we must evaluate
169−applyStaticDatabaseOptimization (MultipleExpr exprs) = do
170− context <- get
171− let optExprs = evalState substateRunner (contextWithEmptyTupleSets context)
172− let errors = lefts optExprs
173− if length errors > 0 then
174− return $ Left (head errors)
175− else
176− return $ Right $ MultipleExpr (rights optExprs)
177− where
178− substateRunner = forM exprs $ \expr -> do
179− --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
180− _ <- evalContextExpr expr
181− applyStaticDatabaseOptimization expr
182− --this error handling could be improved with some lifting presumably
183− --restore original context
184−
109+
185110applyStaticPredicateOptimization :: RestrictionPredicateExpr -> RelationalExprState (Either RelationalError RestrictionPredicateExpr)
186−applyStaticPredicateOptimization predicate = return $ Right predicate
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
test/IsomorphicSchema.hs+13−3
@@ -4,6 +4,9 @@ import ProjectM36.Error
44import ProjectM36.IsomorphicSchema
55import ProjectM36.Relation
66import ProjectM36.RelationalExpression
7+import ProjectM36.DatabaseContextExpression
8+import ProjectM36.RelationalExpressionState
9+import ProjectM36.InclusionDependencyValidation
710import qualified ProjectM36.DatabaseContext as DBC
811import qualified ProjectM36.Attribute as A
912import System.Exit
@@ -30,6 +33,13 @@ assertMaybe x msg = case x of
3033 Nothing -> assertFailure msg >> undefined
3134 Just x' -> pure x'
3235-}
36+
37+--copied from ProjectM36.Client so that Client doesn't need to export this
38+defaultDatabaseContextEvalState :: DatabaseContext -> DatabaseContextEvalState
39+defaultDatabaseContextEvalState context = DatabaseContextEvalState {
40+ dbcontext = context,
41+ constraintValidator = checkConstraints
42+ }
3343
3444-- create some potential schemas which should not be accepted
3545testSchemaValidation :: Test
@@ -92,11 +102,11 @@ testIsoRestrict = TestCase $ do
92102 bobRel <- assertEither (mkRelationFromList empattrs [[TextAtom "Bob", TextAtom ""]])
93103 let schemaBInsertExpr = Insert "employee" (ExistingRelation bobRel)
94104 schemaBInsertExpr' <- assertEither (processDatabaseContextExprInSchema (Schema isomorphsAtoB) schemaBInsertExpr)
95− let postInsertContext = execState (evalContextExpr schemaBInsertExpr') (stateContext schemaA)
96− expectedRel = evalState (evalRelationalExpr (Union (RelationVariable "boss" ()) (RelationVariable "nonboss" ()))) (mkRelationalExprState postInsertContext)
105+ let postInsertState = execState (evalDatabaseContextExpr schemaBInsertExpr') (defaultDatabaseContextEvalState (stateContext schemaA))
106+ expectedRel = evalState (evalRelationalExpr (Union (RelationVariable "boss" ()) (RelationVariable "nonboss" ()))) (mkRelationalExprState (dbcontext postInsertState))
97107 --execute the expression against the schema and compare against the base context
98108 processedExpr <- assertEither (processRelationalExprInSchema (Schema isomorphsAtoB) (RelationVariable "employee" ()))
99− let processedRel = evalState (evalRelationalExpr processedExpr) (mkRelationalExprState postInsertContext)
109+ let processedRel = evalState (evalRelationalExpr processedExpr) (mkRelationalExprState (dbcontext postInsertState))
100110 assertEqual "insert bob boss" expectedRel processedRel
101111
102112testIsoUnion :: Test
test/Relation/Basic.hs+1
@@ -6,6 +6,7 @@ import ProjectM36.DateExamples
66import ProjectM36.DataTypes.Primitive
77import ProjectM36.RelationalExpression
88import ProjectM36.Tuple
9+import ProjectM36.RelationalExpressionState
910import qualified ProjectM36.DatabaseContext as DBC
1011import Control.Monad.State
1112import qualified ProjectM36.Attribute as A
test/Relation/StaticOptimizer.hs+26−4
@@ -3,27 +3,36 @@ import ProjectM36.Relation
33import ProjectM36.RelationalExpression
44import ProjectM36.DateExamples
55import ProjectM36.TupleSet
6−import ProjectM36.StaticOptimizer
6+import ProjectM36.StaticOptimizer.DatabaseContextExpression
7+import ProjectM36.StaticOptimizer.InclusionDependencies
8+import ProjectM36.StaticOptimizer.RelationalExpression
9+import ProjectM36.InclusionDependencyValidation
10+import ProjectM36.RelationalExpressionState
11+import ProjectM36.DatabaseContextExpression
12+import ProjectM36.Key
713import System.Exit
814import Control.Monad.State
915import Test.HUnit
16+import qualified Data.Set as S
1017
1118main :: IO ()
1219main = do
1320 tcounts <- runTestTT (TestList tests)
1421 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
1522 where
16− tests = relationOptTests ++ databaseOptTests
23+ tests = relationOptTests ++ databaseOptTests ++ [testIncDepOptimizer]
24+ dateDBState = DatabaseContextEvalState { dbcontext = dateExamples,
25+ constraintValidator = checkConstraints }
1726 optTest optfunc estate testparams = map (\(name, expr, unopt) -> TestCase $ assertEqual name expr (evalState (optfunc unopt) estate)) $ testparams
1827 relvar nam = RelationVariable nam ()
19− relationOptTests = optTest applyStaticRelationalOptimization (mkRelationalExprState dateExamples) [
28+ relationOptTests = optTest applyStaticRelationalOptimization (mkRelationalExprState dateExamples) [
2029 ("StaticProject", Right $ relvar "s", Project (AttributeNames (attributeNames suppliersRel)) (relvar "s")),
2130 ("StaticUnion", Right $ relvar "s", Union (relvar "s") (relvar "s")),
2231 ("StaticJoin", Right $ relvar "s", Join (relvar "s") (relvar "s")),
2332 ("StaticRestrictTrue", Right $ relvar "s", Restrict TruePredicate (relvar "s")),
2433 ("StaticRestrictFalse", Right $ MakeStaticRelation (attributes suppliersRel) emptyTupleSet, Restrict (NotPredicate TruePredicate) (relvar "s"))
2534 ]
26− databaseOptTests = optTest applyStaticDatabaseOptimization dateExamples [
35+ databaseOptTests = optTest applyStaticDatabaseOptimization dateDBState [
2736 ("StaticAssign",
2837 Right $ Assign "z" (relvar "s"),
2938 Assign "z" (Restrict TruePredicate (relvar "s"))
@@ -34,3 +43,16 @@ main = do
3443 MultipleExpr [Assign "z" (Restrict TruePredicate (relvar "s")),
3544 Assign "z" (Restrict TruePredicate (relvar "s"))])
3645 ]
46+
47+-- test that the static optimizer doesn't eliminate inclusion dependencies improperly
48+testIncDepOptimizer :: Test
49+testIncDepOptimizer = TestCase $ do
50+ --simple case: an insert not mentioning the incdep need not be validated
51+ let incDep1 = inclusionDependencyForKey (AttributeNames (S.singleton "attrX")) (RelationVariable "rv" ())
52+ (InclusionDependency i1 _) = incDep1
53+ putStrLn (show i1)
54+ putStrLn (show (relationVariableNames i1))
55+ assertEqual "relevant relvar names" (S.singleton "rv") (relvarReferences incDep1)
56+ assertEqual "irrelevant insert" NoValidationNeeded (inclusionDependencyValidation (Insert "rv2" (RelationVariable "x" ())) incDep1)
57+ assertEqual "relevant insert" ValidationNeeded (inclusionDependencyValidation (Insert "rv" (RelationVariable "x" ())) incDep1)
58+
test/TransactionGraph/Merge.hs+13−6
@@ -9,7 +9,8 @@ import ProjectM36.Error
99import qualified Data.ByteString.Lazy as BS
1010import System.Exit
1111import Data.Word
12−import ProjectM36.RelationalExpression
12+import ProjectM36.DatabaseContextExpression
13+import ProjectM36.InclusionDependencyValidation
1314import qualified ProjectM36.DisconnectedTransaction as Discon
1415import qualified Data.UUID as U
1516import qualified Data.Set as S
@@ -38,10 +39,16 @@ root 1
3839|--- branchA aaaa
3940|--- branchB bbbb
4041-}
41−
4242createTrans :: TransactionId -> TransactionInfo -> DatabaseContext -> Transaction
4343createTrans tid info ctx = Transaction tid info (Schemas ctx M.empty)
4444
45+--copied from ProjectM36.Client so that Client doesn't need to export this
46+defaultDatabaseContextEvalState :: DatabaseContext -> DatabaseContextEvalState
47+defaultDatabaseContextEvalState context = DatabaseContextEvalState {
48+ dbcontext = context,
49+ constraintValidator = checkConstraints
50+ }
51+
4552basicTransactionGraph :: IO TransactionGraph
4653basicTransactionGraph = do
4754 let bsGraph = bootstrapTransactionGraph uuidRoot DBC.empty --dateExamples
@@ -113,10 +120,10 @@ testSubGraphToFirstAncestorMoreTransactions = TestCase $ do
113120
114121 -- add another relvar to branchB
115122 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
116− updatedBranchBContext <- case runState (evalContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (concreteDatabaseContext branchBTrans) of
123+ updatedBranchBContext <- case runState (evalDatabaseContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (defaultDatabaseContextEvalState (concreteDatabaseContext branchBTrans)) of
117124 (Just err, _) -> assertFailure (show err) >> undefined
118125 (Nothing, context) -> pure context
119− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty) updatedBranchBContext) graph
126+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty) (dbcontext updatedBranchBContext)) graph
120127 branchBTrans' <- assertMaybe (transactionForHead "branchB" graph') "failed to get branchB head"
121128 assertEqual "branchB id 3" (fakeUUID 3) (transactionId branchBTrans')
122129
@@ -140,10 +147,10 @@ testSelectedBranchMerge = TestCase $ do
140147
141148 -- add another relvar to branchB
142149 branchBTrans <- assertMaybe (transactionForHead "branchB" graph) "failed to get branchB head"
143− updatedBranchBContext <- case runState (evalContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (concreteDatabaseContext branchBTrans) of
150+ updatedBranchBContext <- case runState (evalDatabaseContextExpr (Assign "branchBOnlyRelvar" (ExistingRelation relationTrue))) (defaultDatabaseContextEvalState (concreteDatabaseContext branchBTrans)) of
144151 (Just err, _) -> assertFailure (show err) >> undefined
145152 (Nothing, context) -> pure context
146− (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty) updatedBranchBContext) graph
153+ (_, graph') <- addTransaction "branchB" (createTrans (fakeUUID 3) (TransactionInfo (transactionId branchBTrans) S.empty) (dbcontext updatedBranchBContext)) graph
147154 --create the merge transaction in the graph
148155 let eGraph' = mergeTransactions (fakeUUID 4) (fakeUUID 10) (SelectedBranchMergeStrategy "branchA") ("branchA", "branchB") graph'
149156
test/TutorialD/Interpreter.hs+1−1
@@ -391,6 +391,6 @@ testFunctionalDependencies = TestCase $ do
391391 --insert a new, valid tuple
392392 executeTutorialD sessionId dbconn "insert s relation{tuple{city \"Boston\", s# \"S6\", sname \"Stevens\", status 30}}"
393393 --insert an constraint-violating tuple
394− let expectedError = InclusionDependencyCheckError "sname_status_A"
394+ let expectedError = MultipleErrors (map InclusionDependencyCheckError ["sname_status_A", "sname_status_B"])
395395 expectTutorialDErr sessionId dbconn expectedError "insert s relation{tuple{city \"Boston\", s# \"S7\", sname \"Jones\", status 20}}"
396396