1{-# LANGUAGE OverloadedStrings #-}
2import TutorialD.Interpreter.DatabaseExpr
3import TutorialD.Interpreter
4import TutorialD.Interpreter.Base
6import ProjectM36.RelationalExpression
7import ProjectM36.Relation
9import ProjectM36.TupleSet
10import ProjectM36.Error
12import qualified ProjectM36.Base as Base
13import ProjectM36.TransactionGraph
14import ProjectM36.Client
16import qualified ProjectM36.Attribute as A
17import qualified Data.Map as M
19import Data.Maybe (isJust)
21import qualified Data.Vector as V
22import Data.Text.Encoding as TE
23import Data.Typeable (Proxy(..))
24import Data.Text (Text)
25import Data.Time.Clock (UTCTime(..))
26import Data.Time.Calendar (fromGregorian)
27import Data.Interval (interval, Interval, Extended(Finite))
28import Control.Concurrent.STM
32 tcounts <- runTestTT (TestList tests)
33 if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
35 tests = map (\(tutd, expected) -> TestCase $ assertTutdEqual basicDatabaseContext expected tutd) simpleRelTests ++ map (\(tutd, expected) -> TestCase $ assertTutdEqual dateExamples expected tutd) dateExampleRelTests ++ [transactionGraphBasicTest, transactionGraphAddCommitTest, transactionRollbackTest, transactionJumpTest, transactionBranchTest, simpleJoinTest]
36 simpleRelTests = [("x:=true", Right relationTrue),
37 ("x:=false", Right relationFalse),
38 ("x:=true union false", Right relationTrue),
39 ("x:=true; x:=false", Right relationFalse),
40 ("x:=relation{a int}", mkRelation simpleAAttributes emptyTupleSet),
41 ("x:=relation{c int} rename {c as d}", mkRelation simpleBAttributes emptyTupleSet),
42 ("y:=relation{b int, c int}; x:=y{c}", mkRelation simpleProjectionAttributes emptyTupleSet),
43 ("x:=relation{tuple{a \"spam\", b 5}}", mkRelation simpleCAttributes $ RelationTupleSet [(RelationTuple simpleCAttributes) (V.fromList [stringAtom "spam", intAtom 5])]),
44 ("constraint failc true in false; x:=true", Left $ InclusionDependencyCheckError "failc"),
45 ("x:=y; x:=true", Left $ RelVarNotDefinedError "y"),
46 ("x:=true where true", Right relationTrue),
47 ("x:=true where false", Right relationFalse),
48 ("x:=true where true or false", Right relationTrue),
49 ("x:=true where false or false", Right relationFalse),
50 ("x:=true where true and false", Right relationFalse),
51 ("x:=true where true and true", Right relationTrue),
52 ("x:=true=true", Right relationTrue),
53 ("x:=true=false", Right relationFalse),
54 ("x:=true; undefine x", Left (RelVarNotDefinedError "x")),
55 ("x:=relation {b int, a char}; insert x relation{tuple{b 5, a \"spam\"}}", mkRelationFromTuples simpleCAttributes [RelationTuple simpleCAttributes $ V.fromList[stringAtom "spam", intAtom 5]]),
56 -- test nested relation constructor
57 ("x:=relation{tuple{a 5, b relation{tuple{a 6}}}}", mkRelation nestedRelationAttributes $ RelationTupleSet [RelationTuple nestedRelationAttributes (V.fromList [intAtom 5, Atom (Relation simpleAAttributes $ RelationTupleSet [RelationTuple simpleAAttributes $ V.fromList [intAtom 6]])])]),
58 ("x:=relation{tuple{b 5,a \"spam\"},tuple{b 6,a \"sam\"}}; delete x where b=6", mkRelation simpleCAttributes $ RelationTupleSet [RelationTuple simpleCAttributes (V.fromList [stringAtom "spam", intAtom 5])]),
59 ("x:=relation{tuple{a 5}} : {b:=@a}", mkRelation simpleDAttributes $ RelationTupleSet [RelationTuple simpleDAttributes (V.fromList [intAtom 5, intAtom 5])]),
60 ("x:=relation{tuple{a 5}} : {b:=6}", mkRelationFromTuples simpleDAttributes [RelationTuple simpleDAttributes (V.fromList [intAtom 5, intAtom 6])]),
61 ("x:=relation{tuple{a 5}} : {b:=add(@a,5)}", mkRelationFromTuples simpleDAttributes [RelationTuple simpleDAttributes (V.fromList [intAtom 5, intAtom 10])]),
62 ("x:=relation{tuple{a 5}} : {b:=add(@a,\"spam\")}", Left (AtomFunctionTypeError "add" 2 intAtomType stringAtomType)),
63 ("x:=relation{tuple{a 5}} : {b:=add(add(@a,2),5)}", mkRelationFromTuples simpleDAttributes [RelationTuple simpleDAttributes (V.fromList [intAtom 5, intAtom 12])])
65 simpleAAttributes = A.attributesFromList [Attribute "a" intAtomType]
66 simpleBAttributes = A.attributesFromList [Attribute "d" intAtomType]
67 simpleCAttributes = A.attributesFromList [Attribute "a" stringAtomType, Attribute "b" intAtomType]
68 simpleDAttributes = A.attributesFromList [Attribute "a" intAtomType, Attribute "b" intAtomType]
69 simpleMaybeTextAttributes = A.attributesFromList [Attribute "a" $ atomTypeForProxy (Proxy :: Proxy (Maybe Text))]
70 simpleMaybeIntAttributes = A.attributesFromList [Attribute "a" $ atomTypeForProxy (Proxy :: Proxy (Maybe Int))]
71 simpleIntervalDateTimeAttributes = A.attributesFromList [Attribute "a" $ atomTypeForProxy (Proxy :: Proxy (Interval UTCTime))]
72 simpleProjectionAttributes = A.attributesFromList [Attribute "c" intAtomType]
73 nestedRelationAttributes = A.attributesFromList [Attribute "a" intAtomType, Attribute "b" (RelationAtomType $ A.attributesFromList [Attribute "a" intAtomType])]
74 extendTestAttributes = A.attributesFromList [Attribute "a" intAtomType, Attribute "b" $ RelationAtomType (attributes suppliersRel)]
75 byteStringAttributes = A.attributesFromList [Attribute "y" byteStringAtomType]
76 groupCountAttrs = A.attributesFromList [Attribute "z" intAtomType]
77 minMaxAttrs = A.attributesFromList [Attribute "S#" stringAtomType, Attribute "z" intAtomType]
78 dateExampleRelTests = [("x:=S where true", Right suppliersRel),
79 ("x:=S where CITY = \"London\"", restrict (\tuple -> atomForAttributeName "CITY" tuple == (Right $ stringAtom "London")) suppliersRel),
80 ("x:=S where false", Right $ Relation (attributes suppliersRel) emptyTupleSet),
81 ("x:=P where COLOR=\"Blue\" and CITY=\"Paris\"", mkRelationFromList (attributes productsRel) [[stringAtom "P5", stringAtom "Cam", stringAtom "Blue", intAtom 12, stringAtom "Paris"]]),
82 ("a:=S; update a (STATUS:=50); x:=a{STATUS}", mkRelation (A.attributesFromList [Attribute "STATUS" intAtomType]) (RelationTupleSet [mkRelationTuple (A.attributesFromList [Attribute "STATUS" intAtomType]) (V.fromList [intAtom 50])])),
84 ("x:=((S : {STATUS2 := add(10,@STATUS)}) where STATUS2=add(10,@STATUS)){CITY,S#,SNAME,STATUS}", Right suppliersRel),
85 ("x:=S; update x where SNAME=\"Blake\" (CITY:=\"Boston\")", relMap (\tuple -> if atomForAttributeName "SNAME" tuple == (Right $ stringAtom "Blake") then updateTuple (M.singleton "CITY" (stringAtom "Boston")) tuple else tuple) suppliersRel),
86 ("x:=relation{tuple{a 5}} : {b:=S}", mkRelation extendTestAttributes (RelationTupleSet [mkRelationTuple extendTestAttributes (V.fromList [intAtom 5, Atom suppliersRel])])),
87 --relatom function tests
88 ("x:=((S group ({CITY} as y)):{z:=count(@y)}){z}", mkRelation groupCountAttrs (RelationTupleSet [mkRelationTuple groupCountAttrs (V.singleton $ intAtom 1)])),
89 ("x:=(SP group ({S#} as y)) ungroup y", Right supplierProductsRel),
90 ("x:=((SP{S#,QTY}) group ({QTY} as x):{z:=max(@x)}){S#,z}", mkRelationFromList minMaxAttrs (map (\(s,i) -> [stringAtom s,intAtom i]) [("S1", 400), ("S2", 400), ("S3", 200), ("S4", 400)])),
91 ("x:=((SP{S#,QTY}) group ({QTY} as x):{z:=min(@x)}){S#,z}", mkRelationFromList minMaxAttrs (map (\(s,i) -> [stringAtom s,intAtom i]) [("S1", 100), ("S2", 300), ("S3", 200), ("S4", 200)])),
92 ("x:=((SP{S#,QTY}) group ({QTY} as x):{z:=sum(@x)}){S#,z}", mkRelationFromList minMaxAttrs (map (\(s,i) -> [stringAtom s,intAtom i]) [("S1", 1000), ("S2", 700), ("S3", 200), ("S4", 900)])),
93 --boolean function restriction
94 ("x:=S where ^lt(@STATUS,20)", mkRelationFromList (attributes suppliersRel) [[stringAtom "S2", stringAtom "Jones", intAtom 10, stringAtom "Paris"]]),
95 ("x:=S where ^gt(@STATUS,20)", mkRelationFromList (attributes suppliersRel) [[stringAtom "S3", stringAtom "Blake", intAtom 30, stringAtom "Paris"],
96 [stringAtom "S5", stringAtom "Adams", intAtom 30, stringAtom "Athens"]]),
97 ("x:=S where ^sum(@STATUS)", Left $ AtomTypeMismatchError intAtomType boolAtomType),
98 ("x:=S where ^not(gte(@STATUS,20))", mkRelationFromList (attributes suppliersRel) [[stringAtom "S2", stringAtom "Jones", intAtom 10, stringAtom "Paris"]]),
99 --test "all but" attribute inversion syntax
100 ("x:=S{all but S#} = S{CITY,SNAME,STATUS}", Right $ relationTrue),
102 ("x:=S; key testconstraint {S#,CITY} x; insert x relation{tuple{CITY \"London\", S# \"S1\", SNAME \"gonk\", STATUS 50}}", Left (InclusionDependencyCheckError "testconstraint")),
103 ("y:=S; key testconstraint {S#} y; insert y relation{tuple{CITY \"London\", S# \"S6\", SNAME \"gonk\", STATUS 50}}; x:=y{S#} = S{S#} union relation{tuple{S# \"S6\"}}", Right $ relationTrue),
104 --test binary bytestring data type
105 ("x:=relation{tuple{y \"dGVzdGRhdGE=\"::bytestring}}", mkRelationFromList byteStringAttributes [[Atom (TE.encodeUtf8 "testdata")]]),
107 ("x:=relation{tuple{a Just \"spam\"::maybe char}}", mkRelationFromList simpleMaybeTextAttributes [[Atom (Just "spam" :: Maybe Text)]]),
109 ("x:=relation{tuple{a Just 3::maybe int}}", mkRelationFromList simpleMaybeIntAttributes [[Atom (Just 3 :: Maybe Int)]]),
110 --test datetime interval
111 ("x:=relation{tuple{a interval_datetime((\"2015-01-01 00:00:00\",\"2016-01-01 00:00:00\"])}}", mkRelationFromList simpleIntervalDateTimeAttributes [
112 [Atom $ interval (Finite (UTCTime (fromGregorian 2015 1 1) 0), False)
113 (Finite (UTCTime (fromGregorian 2016 1 1) 0), True)]
117assertTutdEqual :: DatabaseContext -> Either RelationalError Relation -> String -> Assertion
118assertTutdEqual databaseContext expected tutd = assertEqual tutd expected interpreted
120 interpreted = case interpretDatabaseExpr databaseContext tutd of
122 Right context -> case M.lookup "x" (relationVariables context) of
123 Nothing -> Left $ RelVarNotDefinedError "x"
124 Just rel -> Right rel
126transactionGraphBasicTest :: Test
127transactionGraphBasicTest = TestCase $ do
128 dbconn <- dateExamplesConnection
129 graph <- transactionGraph dbconn
130 assertEqual "validate bootstrapped graph" (validateGraph graph) Nothing
132--add a new transaction to the graph, validate it is in the graph
133transactionGraphAddCommitTest :: Test
134transactionGraphAddCommitTest = TestCase $ do
135 dbconn <- dateExamplesConnection
136 case parseTutorialD "x:=S" of
137 Left err -> assertFailure (show err)
139 result <- evalTutorialD dbconn parsed
141 QuitResult -> assertFailure "quit?"
142 DisplayResult _ -> assertFailure "display?"
143 DisplayIOResult _ -> assertFailure "displayIO?"
144 DisplayErrorResult err -> assertFailure (show err)
145 QuietSuccessResult -> do
146 commit dbconn >>= maybeFail
147 (DisconnectedTransaction _ context) <- disconnectedTransaction dbconn
148 assertEqual "ensure x was added" (M.lookup "x" (relationVariables context)) (Just suppliersRel)
150transactionRollbackTest :: Test
151transactionRollbackTest = TestCase $ do
152 dbconn <- dateExamplesConnection
153 graph <- transactionGraph dbconn
154 maybeErr <- executeDatabaseContextExpr dbconn (Assign "x" (RelationVariable "S"))
156 Just err -> assertFailure (show err)
158 rollback dbconn >>= maybeFail
159 (DisconnectedTransaction _ context') <- disconnectedTransaction dbconn
160 graph' <- transactionGraph dbconn
161 assertEqual "validate context" Nothing (M.lookup "x" (relationVariables context'))
162 assertEqual "validate graph" graph graph'
164--commit a new transaction with "x" relation, jump to first transaction, verify that "x" is not present
165transactionJumpTest :: Test
166transactionJumpTest = TestCase $ do
167 dbconn <- dateExamplesConnection
168 (DisconnectedTransaction firstUUID _) <- disconnectedTransaction dbconn
169 maybeErr <- executeDatabaseContextExpr dbconn (Assign "x" (RelationVariable "S"))
171 Just err -> assertFailure (show err)
173 commit dbconn >>= maybeFail
175 maybeErr2 <- executeGraphExpr dbconn (JumpToTransaction firstUUID)
177 Just err -> assertFailure (show err)
179 --check that the disconnected transaction does not include "x"
180 (DisconnectedTransaction _ context') <- disconnectedTransaction dbconn
181 assertEqual "ensure x is not present" Nothing (M.lookup "x" (relationVariables context'))
183maybeFail :: (Show a) => Maybe a -> IO ()
184maybeFail (Just err) = assertFailure (show err)
185maybeFail Nothing = return ()
187--branch from the first transaction and verify that there are two heads
188transactionBranchTest :: Test
189transactionBranchTest = TestCase $ do
190 dbconn <- dateExamplesConnection
191 mapM_ (\x -> x >>= maybeFail) [executeGraphExpr dbconn (Branch "test"),
192 executeDatabaseContextExpr dbconn (Assign "x" (RelationVariable "S")),
194 executeGraphExpr dbconn (JumpToHead "master"),
195 executeDatabaseContextExpr dbconn (Assign "y" (RelationVariable "S"))
197 graph <- transactionGraph dbconn
198 assertBool "master branch exists" $ isJust (transactionForHead "master" graph)
199 assertBool "test branch exists" $ isJust (transactionForHead "test" graph)
201simpleJoinTest :: Test
202simpleJoinTest = TestCase $ assertTutdEqual dateExamples joinedRel "x:=S join SP"
204 attrs = A.attributesFromList [Attribute "CITY" stringAtomType,
205 Attribute "QTY" intAtomType,
206 Attribute "P#" stringAtomType,
207 Attribute "S#" stringAtomType,
208 Attribute "SNAME" stringAtomType,
209 Attribute "STATUS" intAtomType]
210 joinedRel = mkRelationFromList attrs [[stringAtom "London", intAtom 100, stringAtom "P6", stringAtom "S1", stringAtom "Smith", intAtom 20],
211 [stringAtom "London", intAtom 400, stringAtom "P3", stringAtom "S1", stringAtom "Smith", intAtom 20],
212 [stringAtom "London", intAtom 400, stringAtom "P5", stringAtom "S4", stringAtom "Clark", intAtom 20],
213 [stringAtom "London", intAtom 300, stringAtom "P1", stringAtom "S1", stringAtom "Smith", intAtom 20],
214 [stringAtom "Paris", intAtom 200, stringAtom "P2", stringAtom "S3", stringAtom "Blake", intAtom 30],
215 [stringAtom "Paris", intAtom 300, stringAtom "P1", stringAtom "S2", stringAtom "Jones", intAtom 10],
216 [stringAtom "London", intAtom 100, stringAtom "P5", stringAtom "S1", stringAtom "Smith", intAtom 20],
217 [stringAtom "London", intAtom 300, stringAtom "P4", stringAtom "S4", stringAtom "Clark", intAtom 20],
218 [stringAtom "Paris", intAtom 400, stringAtom "P2", stringAtom "S2", stringAtom "Jones", intAtom 10],
219 [stringAtom "London", intAtom 200, stringAtom "P2", stringAtom "S1", stringAtom "Smith", intAtom 20],
220 [stringAtom "London", intAtom 200, stringAtom "P4", stringAtom "S1", stringAtom "Smith", intAtom 20],
221 [stringAtom "London", intAtom 200, stringAtom "P2", stringAtom "S4", stringAtom "Clark", intAtom 20]
223transactionGraph :: Connection -> IO TransactionGraph
224transactionGraph (InProcessConnection _ tvar) = atomically $ do
225 (_, graph) <- readTVar tvar
227transactionGraph _ = error "remote connection used"
229disconnectedTransaction :: Connection -> IO DisconnectedTransaction
230disconnectedTransaction (InProcessConnection _ tvar) = atomically $ do
231 (discon, _) <- readTVar tvar
233disconnectedTransaction _ = error "remote connection used"
236inclusionDependencies :: Connection -> M.Map IncDepName InclusionDependency
237inclusionDependencies (InProcessConnection (DisconnectedTransaction _ context)) = inclusionDependencies context
238inclusionDependencies _ = error "remote connection used"
241dateExamplesConnection :: IO (Connection)
242dateExamplesConnection = do
243 dbconn <- connectProjectM36 (InProcessConnectionInfo NoPersistence)
244 let incDeps = Base.inclusionDependencies dateExamples
247 hPutStrLn stderr (show err)
250 mapM_ (\(rvName,rvRel) -> executeDatabaseContextExpr conn (Assign rvName (ExistingRelation rvRel))) (M.toList (relationVariables dateExamples))
251 mapM_ (\(idName,incDep) -> executeDatabaseContextExpr conn (AddInclusionDependency idName incDep)) (M.toList incDeps)
252 --skipping atom functions for now- there are no atom function manipulation operators yet
253 commit conn >>= maybeFail
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list