on master help sign in
implement update operator with simple restriction clause

implement simple Tutorial D actions to allow for "quit" action

commit2f6b8715be160577b976a7775587145fff22c00f authorAgentM <agentm@themactionfaction.com> dateTue 27 Jan 2015, 17:40 (12 years ago) parent6cbd8a3
7 files changed+125−66
Relation.hs+22−26 RelationExpr.hs+28−2 RelationTuple.hs+5 RelationTupleSet.hs+13−9 RelationType.hs+9−1 TutorialDInterpreter.hs+42−25 TutorialDInterpreterTest.hs+6−3
Relation.hs+22−26
@@ -4,6 +4,7 @@ module Relation where
44import qualified Data.Map as M
55import qualified Data.Set as S
66import qualified Data.HashSet as HS
7+import Control.Monad
78
89import RelationType
910import RelationTuple
@@ -12,8 +13,6 @@ import RelationTupleSet
1213import RelationalError
1314import Prelude hiding (join)
1415
15−testTuple1 = mkRelationTuple (S.fromList ["hair"]) (M.fromList [("hair", StringAtom "brown")])
16−
1716attributes :: Relation -> Attributes
1817attributes (Relation attrs _ ) = attrs
1918
@@ -41,24 +40,12 @@ atomValueType (IntAtom _) = IntAtomType
4140atomValueType (RelationAtom rel) = RelationAtomType (attributes rel)
4241
4342mkRelation :: Attributes -> RelationTupleSet -> Either RelationalError Relation
44−mkRelation attrs tupleSet =
43+mkRelation attrs tupleSet = do
4544 --check that all tuples have the same keys
4645 --check that all tuples have keys (1-N) where N is the attribute count
47− if differentTupleCounts
48− then Left $ TupleAttributeCountMismatchError 0
49− else if not (fst (verifyRelationTupleSet tupleSet))
50− then Left $ TupleAttributeTypeMismatchError 0
51− else if attrCountMismatch
52− then Left $ AttributeCountMismatchError 0
53− else
54− Right $ Relation attrs tupleSet
55− where
56− attrCount = M.size attrs
57− tupleCounts = HS.map tupleSize tupleSet
58− attrCountMismatch = if HS.size tupleSet > 0
59− then not $ HS.member attrCount tupleCounts
60− else False --empty tuple set is always OK
61− differentTupleCounts = HS.size tupleCounts > 1
46+ case verifyRelationTupleSet attrs tupleSet of
47+ Left err -> Left err
48+ Right verifiedTupleSet -> return $ Relation attrs verifiedTupleSet
6249
6350relationTrue = Relation M.empty $ HS.singleton (RelationTuple M.empty)
6451relationFalse = Relation M.empty emptyTupleSet
@@ -233,18 +220,27 @@ join (Relation attrs1 tupSet1) (Relation attrs2 tupSet2) = Right $ Relation newA
233220 newAttrs = M.union attrs1 attrs2
234221 newTupSet = HS.foldr tupleSetJoiner emptyTupleSet tupSet1
235222 tupleSetJoiner tuple1 accumulator = HS.union (singleTupleSetJoin tuple1 tupSet2) accumulator
236−
223+
224+--a map should NOT change the structure of a relation, so attributes should be constant
225+relMap :: (RelationTuple -> RelationTuple) -> Relation -> Either RelationalError Relation
226+relMap mapper (Relation attrs tupleSet) = do
227+ case forM (HS.toList tupleSet) typeMapCheck of
228+ Right remappedTupleSet -> Right $ Relation attrs $ HS.fromList remappedTupleSet
229+ Left err -> Left err
230+ where
231+ typeMapCheck tupleIn = do
232+ let remappedTuple = mapper tupleIn
233+ if tupleAttributes remappedTuple == tupleAttributes tupleIn
234+ then Right remappedTuple
235+ else Left $ TupleAttributeTypeMismatchError 0
237236
238−--a map should NOT change the structure of a relation, so attributes should be constant relMap :: (RelationTuple -> RelationTuple) -> Attributes -> Relation -> Either RelationalError Relation
239−relMap mapper newAttributes (Relation attrs tupleSet) = Right $ Relation newAttributes (HS.map mapper tupleSet)
237+--in the future, the mapper should be able to return a RelationalError likely
238+relMogrify :: (RelationTuple -> RelationTuple) -> Attributes -> Relation -> Either RelationalError Relation
239+relMogrify mapper newAttributes (Relation _ tupSet) = mkRelation newAttributes (HS.map mapper tupSet)
240240
241241relFold :: (RelationTuple -> a -> a) -> a -> Relation -> a
242242relFold folder acc (Relation attrs tupleSet) = HS.foldr folder acc tupleSet
243243
244−{- a specialized form of fold which always returns a Relation
245−relTuplesFold :: (RelationTuple -> RelationTupleSet -> RelationTupleSet) -> RelationTupleSet -> AttributeNameSet -> Relation -> Either RelationalError Relation
246−-}
247−
248244--image relation as defined by CJ Date
249245--given tupleA and relationB, return restricted relation where tuple attributes are not the attribues in tupleA but are attributes in relationB and match the tuple's value
250246
@@ -277,7 +273,7 @@ relvarS = mkRelation attributes tupleSet
277273 where
278274 attributes = M.fromList [("S#", Attribute "S#" StringAtomType),
279275 ("SNAME", Attribute "SNAME" StringAtomType),
280− ("STATUS", Attribute "STATUS" StringAtomType),
276+ ("STATUS", Attribute "STATUS" IntAtomType),
281277 ("CITY", Attribute "CITY" StringAtomType)]
282278 tupleSet = HS.fromList $ mkRelationTuples attributes [
283279 M.fromList [("S#", StringAtom "S1") , ("SNAME", StringAtom "Smith"), ("STATUS", IntAtom 20) , ("CITY", StringAtom "London")],
RelationExpr.hs+28−2
@@ -98,7 +98,7 @@ suppliersRel = case mkRelation attributes tupleSet of
9898 where
9999 attributes = M.fromList [("S#", Attribute "S#" StringAtomType),
100100 ("SNAME", Attribute "SNAME" StringAtomType),
101− ("STATUS", Attribute "STATUS" StringAtomType),
101+ ("STATUS", Attribute "STATUS" IntAtomType),
102102 ("CITY", Attribute "CITY" StringAtomType)]
103103 tupleSet = HS.fromList $ mkRelationTuples attributes [
104104 M.fromList [("S#", StringAtom "S1") , ("SNAME", StringAtom "Smith"), ("STATUS", IntAtom 20) , ("CITY", StringAtom "London")],
@@ -193,7 +193,27 @@ evalContextExpr (Delete relVarName) = do
193193 Nothing -> return $ Just (RelVarNotDefinedError relVarName)
194194 Just rel -> setRelVar relVarName (Relation (attributes rel) emptyTupleSet)
195195
196−evalContextExpr (Update relVarName attrAssignments) = undefined
196+
197+--union of restricted+updated portion and the unrestricted+unupdated portion
198+evalContextExpr (Update relVarName attrAssignments restrictionPredicateExpr) = do
199+ state <- get
200+ let relVarTable = relationVariables state
201+ case predicateRestrictionFilter state restrictionPredicateExpr of
202+ Left err -> return $ Just err
203+ Right predicateFunc -> do
204+ case M.lookup relVarName relVarTable of
205+ Nothing -> return $ Just (RelVarNotDefinedError relVarName)
206+ Just rel -> case makeUpdatedRel rel of
207+ Left err -> return $ Just err
208+ Right updatedRel -> setRelVar relVarName updatedRel
209+ where
210+ makeUpdatedRel rel = do
211+ restrictedPortion <- restrict predicateFunc rel
212+ unrestrictedPortion <- restrict (not . predicateFunc) rel
213+ updatedPortion <- relMap tupleUpdater restrictedPortion
214+ union updatedPortion unrestrictedPortion
215+ tupleUpdater = \(RelationTuple tupMap) -> RelationTuple $ M.union attrAssignments tupMap
216+
197217
198218evalContextExpr (AddInclusionDependency dep) = do
199219 state <- get
@@ -262,6 +282,12 @@ predicateRestrictionFilter context (OrPredicate expr1 expr2) = do
262282 expr1v <- predicateRestrictionFilter context expr1
263283 expr2v <- predicateRestrictionFilter context expr2
264284 return $ \x -> expr1v x || expr2v x
285+
286+predicateRestrictionFilter context TruePredicate = Right $ \x -> True
287+
288+predicateRestrictionFilter context (NotPredicate expr) = do
289+ exprv <- predicateRestrictionFilter context expr
290+ return $ \x -> not (exprv x)
265291
266292predicateRestrictionFilter context (RelationalExprPredicate relExpr) = case runState (evalRelationalExpr relExpr) context of
267293 (Left err, _) -> Left err
RelationTuple.hs+5
@@ -12,6 +12,11 @@ tupleSize (RelationTuple m) = M.size m
1212tupleAttributeNameSet :: RelationTuple -> S.Set AttributeName
1313tupleAttributeNameSet (RelationTuple tupMap) = M.keysSet tupMap
1414
15+tupleAttributes :: RelationTuple -> Attributes
16+tupleAttributes (RelationTuple tupMap) = M.mapWithKey mapper tupMap
17+ where
18+ mapper key atom = Attribute key (atomTypeForAtom atom)
19+
1520tupleAtomForAttribute :: RelationTuple -> AttributeName -> Atom
1621tupleAtomForAttribute (RelationTuple tupMap) attrName = tupMap M.! attrName
1722
RelationTupleSet.hs+13−9
@@ -1,20 +1,24 @@
11module RelationTupleSet where
22import RelationType
3+import RelationTuple
4+import RelationalError
35import qualified Data.Map as M
46import qualified Data.Set as S
57import qualified Data.HashSet as HS
8+import Control.Monad
69
710emptyTupleSet = HS.empty
811emptyAttributeSet = M.empty
912
1013--ensure that all maps have the same keys and key count
1114
12−verifyRelationTupleSet :: RelationTupleSet -> (Bool,String)
13−verifyRelationTupleSet tupleSet = if HS.size failedTupleSet > 0
14− then (False,"Tuple count or key mismatch: " ++ show failedTupleSet)
15− else (True, "")
16− where
17− allKeys = HS.foldr allKeysFolder S.empty tupleSet
18− allKeysFolder (RelationTuple m) = (S.union . M.keysSet) m
19− failedTupleSet = HS.filter (\(RelationTuple m) -> (M.keysSet m) /= allKeys) tupleSet
20−
15+verifyRelationTupleSet :: Attributes -> RelationTupleSet -> Either RelationalError RelationTupleSet
16+verifyRelationTupleSet attrs tupleSet = do
17+ --check that all tuples have the same types
18+ tupleList <- forM (HS.toList tupleSet) verifyTuple
19+ return $ HS.fromList tupleList
20+ where
21+ verifyTuple tuple = if tupleAttributes tuple == attrs then
22+ Right tuple
23+ else
24+ Left $ TupleAttributeTypeMismatchError 0
RelationType.hs+9−1
@@ -9,10 +9,16 @@ import Control.Monad.State hiding (join)
99data Atom = StringAtom String |
1010 IntAtom Int |
1111 RelationAtom Relation deriving (Show, Eq)
12+
1213
1314data AtomType = StringAtomType |
1415 IntAtomType |
1516 RelationAtomType Attributes deriving (Eq, Show)
17+
18+atomTypeForAtom :: Atom -> AtomType
19+atomTypeForAtom (StringAtom _) = StringAtomType
20+atomTypeForAtom (IntAtom _) = IntAtomType
21+atomTypeForAtom (RelationAtom (Relation attributes _)) = RelationAtomType attributes
1622
1723type AttributeName = String
1824type AtomName = String
@@ -72,7 +78,7 @@ data DatabaseExpr where
7278 Assign :: String -> RelationalExpr -> DatabaseExpr
7379 Insert :: String -> RelationalExpr -> DatabaseExpr
7480 Delete :: String -> DatabaseExpr -- needs restriction support
75− Update :: String -> M.Map String RelationalExpr -> DatabaseExpr -- needs restriction support
81+ Update :: String -> M.Map String Atom -> RestrictionPredicateExpr -> DatabaseExpr -- needs restriction support
7682 AddInclusionDependency :: InclusionDependency -> DatabaseExpr
7783 MultipleExpr :: [DatabaseExpr] -> DatabaseExpr
7884 deriving (Show)
@@ -80,8 +86,10 @@ data DatabaseExpr where
8086type DatabaseState a = State DatabaseContext a
8187
8288data RestrictionPredicateExpr where
89+ TruePredicate :: RestrictionPredicateExpr
8390 AndPredicate :: RestrictionPredicateExpr -> RestrictionPredicateExpr -> RestrictionPredicateExpr
8491 OrPredicate :: RestrictionPredicateExpr -> RestrictionPredicateExpr -> RestrictionPredicateExpr
92+ NotPredicate :: RestrictionPredicateExpr -> RestrictionPredicateExpr
8593 RelationalExprPredicate :: RelationalExpr -> RestrictionPredicateExpr --type must be same as true and false relations (no attributes)
8694 AttributeEqualityPredicate :: AttributeName -> Atom -> RestrictionPredicateExpr -- relationalexpr must result in relation with single tuple
8795 deriving (Show)
TutorialDInterpreter.hs+42−25
@@ -17,7 +17,7 @@ import qualified Data.List as L
1717import Control.Applicative (liftA, (<*), (*>))
1818import Control.Monad.State
1919import System.Console.Readline
20−
20+import System.IO
2121
2222lexer :: Token.TokenParser ()
2323lexer = Token.makeTokenParser tutD
@@ -184,9 +184,9 @@ updateP :: Parser DatabaseExpr
184184updateP = do
185185 reservedOp "update"
186186 relVarName <- identifier
187− -- where clause
187+ predicate <- option TruePredicate (reservedOp "where" *> restrictionPredicateP <* spaces)
188188 attributeAssignments <- liftM M.fromList $ parens (sepBy attributeAssignment comma)
189− return $ Update relVarName attributeAssignments
189+ return $ Update relVarName attributeAssignments predicate
190190
191191constraintP :: Parser DatabaseExpr
192192constraintP = do
@@ -197,12 +197,12 @@ constraintP = do
197197 superset <- relExpr
198198 return $ AddInclusionDependency (InclusionDependency constraintName subset superset)
199199
200−attributeAssignment :: Parser (String, RelationalExpr)
200+attributeAssignment :: Parser (String, Atom)
201201attributeAssignment = do
202202 attrName <- identifier
203203 reservedOp ":="
204− relExpr <- relExpr
205− return $ (attrName, relExpr)
204+ atom <- stringAtomP <|> intAtomP
205+ return $ (attrName, atom)
206206
207207parseString :: String -> Either RelationalError DatabaseExpr
208208parseString str = case parse multipleDatabaseExpr "" str of
@@ -213,6 +213,7 @@ data TutorialDOperator where
213213 ShowRelation :: RelationalExpr -> TutorialDOperator
214214 ShowRelationType :: RelationalExpr -> TutorialDOperator
215215 ShowConstraint :: String -> TutorialDOperator
216+ Quit :: TutorialDOperator
216217 deriving (Show)
217218
218219typeP :: Parser TutorialDOperator
@@ -227,6 +228,11 @@ showRelP = do
227228 expr <- relExpr
228229 return $ ShowRelation expr
229230
231+quitP :: Parser TutorialDOperator
232+quitP = do
233+ reservedOp ":q"
234+ return Quit
235+
230236showConstraintsP :: Parser TutorialDOperator
231237showConstraintsP = do
232238 reservedOp ":c"
@@ -237,6 +243,7 @@ interpreterOps :: Parser TutorialDOperator
237243interpreterOps = typeP
238244 <|> showRelP
239245 <|> showConstraintsP
246+ <|> quitP
240247
241248showRelationAttributes :: Relation -> String
242249showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribute attrs) ++ "}"
@@ -247,23 +254,30 @@ showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribu
247254 attrs = values (attributes rel)
248255 values m = map snd (M.toAscList m)
249256
250−evalTutorialDOp :: DatabaseContext -> TutorialDOperator -> String
257+data TutorialDOperatorResult = QuitResult |
258+ DisplayResult String |
259+ DisplayErrorResult String |
260+ NoActionResult
261+
262+evalTutorialDOp :: DatabaseContext -> TutorialDOperator -> TutorialDOperatorResult
251263evalTutorialDOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
252− (Right rel, _) -> showRelationAttributes rel
253− (Left err, _) -> show err
264+ (Right rel, _) -> DisplayResult $ showRelationAttributes rel
265+ (Left err, _) -> DisplayErrorResult $ show err
254266
255267evalTutorialDOp context (ShowRelation expr) = do
256268 case runState (evalRelationalExpr expr) context of
257− (Left err, _) -> show err
258− (Right rel, _) -> showRelation rel
269+ (Left err, _) -> DisplayErrorResult $ show err
270+ (Right rel, _) -> DisplayResult $ showRelation rel
259271
260272evalTutorialDOp context (ShowConstraint name) = do
261− show filteredDeps
273+ DisplayResult $ show filteredDeps
262274 where
263275 deps = inclusionDependencies context
264276 filteredDeps = case name of
265277 "" -> deps
266278 name -> HS.filter (\(InclusionDependency n _ _) -> n == name) deps
279+
280+evalTutorialDOp context (Quit) = QuitResult
267281
268282example1 = "relA {a,b, c}"
269283example2 = "relA join relB"
@@ -281,10 +295,10 @@ interpret context tutdstring = case parseString tutdstring of
281295 Right parsed -> runState (evalContextExpr parsed) context
282296
283297-- for interpreter-specific operations
284−interpretOps :: DatabaseContext -> String -> Maybe String
298+interpretOps :: DatabaseContext -> String -> TutorialDOperatorResult
285299interpretOps context instring = case parse interpreterOps "" instring of
286− Left err -> Nothing
287− Right ops -> Just $ evalTutorialDOp context ops
300+ Left err -> NoActionResult
301+ Right ops -> evalTutorialDOp context ops
288302
289303reprLoop :: DatabaseContext -> IO ()
290304reprLoop context = do
@@ -295,28 +309,31 @@ reprLoop context = do
295309 addHistory line
296310
297311 case interpretOps context line of
298− Just out -> do
312+ QuitResult -> return ()
313+ DisplayErrorResult err -> hPutStrLn stderr ("ERR: " ++ err)
314+ DisplayResult out -> do
299315 putStrLn out
300316 reprLoop context
301− Nothing -> return ()
302−
303− let (value, contextup) = interpret context line
304− case value of
305− Nothing -> reprLoop contextup
306− (Just err) -> do
307− putStrLn $ show err
308− reprLoop context
317+ NoActionResult -> do
318+ let (value, contextup) = interpret context line
319+ case value of
320+ Nothing -> reprLoop contextup
321+ (Just err) -> do
322+ hPutStrLn stderr ("ERR:" ++ show err)
323+ reprLoop context
309324
310325restrictionPredicateP :: Parser RestrictionPredicateExpr
311326restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
312327 where
313328 predicateOperators = [
329+ [Prefix (reservedOp "not" >> return NotPredicate)],
314330 [Infix (reservedOp "and" >> return AndPredicate) AssocLeft],
315331 [Infix (reservedOp "or" >> return OrPredicate) AssocLeft]
316332 ]
317333 predicateTerm = parens restrictionPredicateP
318334 <|> try restrictionAttributeEqualityP
319− <|> relationalBooleanExpr
335+ <|> try relationalBooleanExpr
336+
320337
321338relationalBooleanExpr :: Parser RestrictionPredicateExpr
322339relationalBooleanExpr = do
TutorialDInterpreterTest.hs+6−3
@@ -8,6 +8,8 @@ import qualified Data.HashSet as HS
88import qualified Data.Map as M
99import System.Exit
1010
11+import Debug.Trace
12+
1113main = do
1214 counts <- runTestTT (TestList tests)
1315 if errors counts + failures counts > 0 then exitFailure else exitSuccess
@@ -33,9 +35,10 @@ main = do
3335 simpleProjectionAttributes = M.fromList [("c", Attribute "c" IntAtomType)]
3436 dateExampleRelTests = [("x:=S where true", Right s),
3537 ("x:=S where CITY = \"London\"", restrict (\(RelationTuple tupMap) -> tupMap M.! "CITY" == StringAtom "London") s),
36− ("x:=S where false", Right $ Relation (attributes s) HS.empty)]
37− --("x:=S where SNO in S{SNO}", Right s)]
38−
38+ ("x:=S where false", Right $ Relation (attributes s) HS.empty),
39+ ("a:=S; update a (STATUS:=50); x:=a{STATUS}", mkRelation (M.fromList [("STATUS", Attribute "STATUS" IntAtomType)]) (HS.fromList [RelationTuple $ M.fromList [("STATUS", IntAtom 50)]])),
40+ ("x:=S; update x where SNAME=\"Blake\" (CITY:=\"Boston\")", relMap (\(RelationTuple tupMap) -> RelationTuple $ if (traceShowId tupMap) M.! "SNAME" == StringAtom "Blake" then M.insert "CITY" (StringAtom "Boston") tupMap else tupMap) s)
41+ ]
3942
4043testSimple1 = TestCase $ assertTutdEqual basicDatabaseContext "true" (Right relationTrue)
4144