on master help sign in
Merge branch 'transactions'

Conflicts: README.markdown RelationTerm.hs RelationType.hs TutorialDInterpreter.hs TutorialDInterpreterTest.hs

commitce026c64bbc26996720e0c62b3456b6960bd8d46 authorAgentM <agentm@themactionfaction.com> dateSun 19 Apr 2015, 14:42 (12 years ago) parent839ee526cd3cb1
15 files changed+676−87
README.markdown+56−6 Relation.hs+1−1 RelationAttribute.hs+13 RelationExpr.hs+1−2 RelationTerm.hs+19−6 RelationTest.hs+1−1 RelationTupleSet.hs+2−1 RelationType.hs+42−2 RelationalError.hs+9−2 RelationalTransaction.hs+136 RelationalTransactionShow.hs+62 TutorialDInterpreter.hs+224−62 TutorialDInterpreterTest.hs+98−2 project-m36.cabal+1−1 tutd.hs+11−1
README.markdown+56−6
@@ -26,7 +26,7 @@ At this point, there are three types of command which can be executed:
26261. relational algebra expressions such as:
2727
2828 ```
29−TutorialD: :s S join P
29+TutorialD (master): :showExpr S join P
3030┌──────┬─────┬──┬─────┬──┬─────┬──────┬──────┐
3131│CITY │COLOR│P#│PNAME│S#│SNAME│STATUS│WEIGHT│
3232├──────┼─────┼──┼─────┼──┼─────┼──────┼──────┤
@@ -43,32 +43,81 @@ TutorialD: :s S join P
4343└──────┴─────┴──┴─────┴──┴─────┴──────┴──────┘
4444```
4545
46− where ":s" instructs the interpreter to show the resultant relation, "S" and "P" are relation names, and "join" is the join operator.
46+ where ":show" instructs the interpreter to show the resultant relation, "S" and "P" are relation names, and "join" is the join operator.
47472. interpreter-level queries such as:
4848
4949 ```
50−TutorialD: :t S
50+TutorialD (master): :type S
5151{CITY "char", S# "char", SNAME "char", STATUS "char"}
5252```
5353
5454 which shows the Tutorial D type for a relational expression and
5555
5656 ```
57−TutorialD: :c
57+TutorialD (master): :constraints
5858fromList []
5959```
6060which displays database constraints.
6161
6262 ```
63−TutorialD: :p x:=S{CITY,S#,SNAME,STATUS}
63+TutorialD (master): :showplan x:=S{CITY,S#,SNAME,STATUS}
6464Right (Assign "x" (RelationVariable "S"))
6565```
6666which displays the optimized execution plan which would have been executed to evaluate the expression.
6767
68+TutorialD (master): :commit
69+```
70+which commits the current "disconnected" transaction which is similar to the git staging area/index.
71+
72+ ```
73+TutorialD (master): :jumphead master
74+Done.
75+```
76+which changes the current disconnected transaction to reference another head.
77+
78+ ```
79+TutorialD (master): :jump 4a4ee715-297d-49db-bfd1-afaefadc902b
80+Done.
81+```
82+which resets the current "disconnected" transaction to refer to the committed transaction specified by the UUID.
83+
84+ ```
85+TutorialD (master): :branch devel
86+Done.
87+```
88+
89+which creates a new head named "devel" in the transaction graph and resets the current "disconnected" transaction to refer to the
90+
91+ ```
92+TutorialD (devel): :rollback
93+Done.
94+```
95+
96+which resets the current "disconnected" transaction to discard any collected changes.
97+
98+ ```
99+TutorialD (devel): :showGraph
100+┌───────┬────────────────────────────────────┬──────────────────────────────────────┐
101+│current│id │parents │
102+├───────┼────────────────────────────────────┼──────────────────────────────────────┤
103+│1 │1d064d54-cd2a-4a43-822a-cbd18cf7c4e8│┌────────────────────────────────────┐│
104+│ │ ││id ││
105+│ │ │├────────────────────────────────────┤│
106+│ │ ││4a4ee715-297d-49db-bfd1-afaefadc902b││
107+│ │ │└────────────────────────────────────┘│
108+│0 │4a4ee715-297d-49db-bfd1-afaefadc902b│┌──┐ │
109+│ │ ││id│ │
110+│ │ │ │
111+│ │ │└──┘ │
112+└───────┴────────────────────────────────────┴──────────────────────────────────────┘
113+```
114+
115+which displays all committed transactions and their parents. The "current" attribute indicates to which transactions the current "disconnected" transaction refers.
116+
681173. expressions which modify the database context such as:
69118
70119 ```
71−TutorialD: x := S JOIN P
120+TutorialD (master): x := S JOIN P
72121```
73122 which sets the relational variable named "x" to the results of "S join P" if the database constraint checking succeeds.
74123
@@ -77,6 +126,7 @@ TutorialD: x := S JOIN P
77126 ```
78127constraint check_x false in true
79128```
129+
80130 checks that the expression "false" is a subrelation of "true", which, in this case, is always true.
81131
82132## Development
Relation.hs+1−1
@@ -236,7 +236,7 @@ relMap mapper (Relation attrs tupleSet) = do
236236 let remappedTuple = mapper tupleIn
237237 if tupleAttributes remappedTuple == tupleAttributes tupleIn
238238 then Right remappedTuple
239− else Left $ TupleAttributeTypeMismatchError 0
239+ else Left $ TupleAttributeTypeMismatchError (attributesDifference (tupleAttributes tupleIn) attrs)
240240
241241--in the future, the mapper should be able to return a RelationalError likely
242242relMogrify :: (RelationTuple -> RelationTuple) -> Attributes -> Relation -> Either RelationalError Relation
RelationAttribute.hs+13
@@ -3,6 +3,7 @@ import RelationType
33import qualified Data.Map as M
44import qualified Data.Set as S
55import qualified Data.List as L
6+import Debug.Trace
67
78renameAttribute :: AttributeName -> Attribute -> Attribute
89renameAttribute newAttrName (Attribute oldAttrName typeo) = Attribute newAttrName typeo
@@ -33,3 +34,15 @@ matchingAttributeNameSet = S.intersection
3334-- this is sorted so the tuples know in which order to output- the ordering is arbitrary
3435sortedAttributeNameList attrNameSet= L.sort $ S.toList attrNameSet
3536
37+-- take two attribute sets and return an attribute set with the attributes which do not match
38+-- bug: missing attributes don't show up (?)
39+attributesDifference :: Attributes -> Attributes -> Attributes
40+attributesDifference attrA attrB = trace (show attrA ++ "\n" ++ show attrB) (M.union mismatchA missingB)
41+ where
42+ mismatchA = M.foldrWithKey folder M.empty attrA
43+ missingB = M.difference attrA attrB
44+ folder :: AttributeName -> Attribute -> Attributes -> Attributes
45+ folder attrName attrinA acc = if M.notMember attrName attrB || M.lookup attrName attrB /= Just attrinA then
46+ M.insert attrName attrinA acc
47+ else
48+ acc
RelationExpr.hs+1−2
@@ -82,8 +82,6 @@ evalRelationalExpr (Equals relExprA relExprB) = do
8282 Left err -> return $ Left err
8383 Right relB -> return $ Right $ if relA == relB then relationTrue else relationFalse
8484
85−
86−
8785emptyDatabaseContext :: DatabaseContext
8886emptyDatabaseContext = DatabaseContext { inclusionDependencies = HS.empty,
8987 relationVariables = M.empty}
@@ -339,3 +337,4 @@ predicateRestrictionFilter context (AttributeEqualityPredicate attrName atom) =
339337
340338
341339
340+
RelationTerm.hs+19−6
@@ -1,6 +1,7 @@
11--writes Relation to a String suitable for terminal output
22module RelationTerm where
33import RelationType
4+import RelationTuple
45import Relation
56import qualified Data.Set as S
67import qualified Data.Map as M
@@ -48,10 +49,15 @@ cellLocations tab@(header, body) = (maxWidths, maxHeights)
4849 maxHeights = map (L.maximumBy compare) rowHeights
4950 mergeMax a b = map (\(a,b) -> max a b) (zip a b)
5051
52+--the normal "lines" function returns an empty list for an empty string which is not what we want
53+breakLines :: String -> [String]
54+breakLines "" = [""]
55+breakLines x = lines x
56+
5157cellSizes :: Table -> [([Int], [Int])]
52−cellSizes (header, body) = map (\row -> (map maxRowWidth row, map (length . lines) row)) allRows
58+cellSizes (header, body) = map (\row -> (map maxRowWidth row, map (length . breakLines) row)) allRows
5359 where
54− maxRowWidth row = L.maximumBy compare (map length (lines row))
60+ maxRowWidth row = L.maximumBy compare (map length (breakLines row))
5561 allRows = [header] ++ body
5662
5763relationAsTable :: Relation -> Table
@@ -87,7 +93,7 @@ renderHBar left middle end columnLocations = left ++ concat (L.intersperse middl
8793leftPaddedString :: Int -> Int -> String -> String
8894leftPaddedString lineNum size str = paddedLines !! lineNum
8995 where
90− paddedLines = map (\s -> s ++ repeatString (size - length s) " ") (lines str ++ repeat "")
96+ paddedLines = map (\s -> s ++ repeatString (size - length s) " ") (breakLines str ++ repeat "")
9197
9298renderRow :: [Cell] -> [Int] -> Int -> String -> String
9399renderRow cells columnLocations rowHeight interspersed = unlines $ map renderOneLine [0..rowHeight-1]
@@ -116,6 +122,13 @@ showRelation rel
116122 | otherwise = renderTable (relationAsTable rel)
117123
118124
119−--simpleExample = s
120−--groupedExample = case group (S.fromList ["CITY"]) "CITYREL" s of {Right rel -> rel}
121−
125+simpleExample = s
126+groupedExample = case group (S.fromList ["CITY"]) "CITYREL" s of {Right rel -> rel}
127+
128+emptyStringExample = case mkRelation attributes tupleSet of
129+ Right x -> x
130+ _ -> undefined
131+ where
132+ attributes = M.fromList [("x", Attribute "x" StringAtomType)]
133+ tupleSet = HS.singleton $ mkRelationTuple (S.fromList ["x"]) (M.singleton "x" $ StringAtom "")
134+
RelationTest.hs+1−1
@@ -41,7 +41,7 @@ validateAttrNamesMatchTupleAttrNames rel@(Relation _ tupMapSet)
4141validateAttrTypesMatchTupleAttrTypes :: Relation -> Either RelationalError Relation
4242validateAttrTypesMatchTupleAttrTypes rel@(Relation _ tupMapSet)
4343 | HS.null invalidSet = Right rel
44− | otherwise = Left (TupleAttributeTypeMismatchError 0)
44+ | otherwise = Left (TupleAttributeTypeMismatchError M.empty) --fix to include invalid attrs
4545 where
4646 tupleTypeCheck (RelationTuple tupMap) = M.null $ M.filterWithKey attrTypeCheck tupMap
4747 attrTypeCheck attrName atomValue = case attributeTypeForName attrName rel of
RelationTupleSet.hs+2−1
@@ -1,6 +1,7 @@
11module RelationTupleSet where
22import RelationType
33import RelationTuple
4+import RelationAttribute
45import RelationalError
56import qualified Data.Map as M
67import qualified Data.HashSet as HS
@@ -20,4 +21,4 @@ verifyRelationTupleSet attrs tupleSet = do
2021 verifyTuple tuple = if tupleAttributes tuple == attrs then
2122 Right tuple
2223 else
23− Left $ TupleAttributeTypeMismatchError 0
24+ Left $ TupleAttributeTypeMismatchError (attributesDifference (tupleAttributes tuple) attrs)
RelationType.hs+42−2
@@ -5,6 +5,7 @@ import qualified Data.HashSet as HS
55import qualified Data.Hashable as Hash
66import qualified Data.Set as S
77import Control.Monad.State hiding (join)
8+import Data.UUID (UUID)
89
910data Atom = StringAtom String |
1011 IntAtom Int |
@@ -63,7 +64,7 @@ data DatabaseContext = DatabaseContext {
6364 inclusionDependencies :: HS.HashSet InclusionDependency,
6465 relationVariables :: M.Map String Relation
6566 } deriving (Show)
66−
67+
6768data InclusionDependency = InclusionDependency String RelationalExpr RelationalExpr deriving (Show)
6869
6970instance Hash.Hashable InclusionDependency where
@@ -93,4 +94,43 @@ data RestrictionPredicateExpr where
9394 NotPredicate :: RestrictionPredicateExpr -> RestrictionPredicateExpr
9495 RelationalExprPredicate :: RelationalExpr -> RestrictionPredicateExpr --type must be same as true and false relations (no attributes)
9596 AttributeEqualityPredicate :: AttributeName -> Atom -> RestrictionPredicateExpr -- relationalexpr must result in relation with single tuple
96− deriving (Show, Eq)
97+ deriving (Show, Eq)
98+
99+-- child + parent links
100+-- the string represents the branch name and can be used to find the named HEADs
101+type HeadName = String
102+
103+type TransactionHeads = M.Map HeadName Transaction
104+
105+data TransactionGraph = TransactionGraph TransactionHeads (S.Set Transaction)
106+ deriving (Show, Eq)
107+
108+transactionsForGraph :: TransactionGraph -> S.Set Transaction
109+transactionsForGraph (TransactionGraph _ t) = t
110+
111+transactionHeadsForGraph :: TransactionGraph -> TransactionHeads
112+transactionHeadsForGraph (TransactionGraph heads _) = heads
113+
114+data TransactionInfo = TransactionInfo UUID (S.Set UUID) | -- 1 parent + n children
115+ MergeTransactionInfo UUID UUID (S.Set UUID) -- 2 parents, n children
116+ deriving(Show)
117+
118+data Transaction = Transaction UUID TransactionInfo DatabaseContext -- self uuid
119+ deriving (Show)
120+
121+--represents an "in-progress" transaction which has not yet been added to the transaction graph
122+--one the transaction is "complete", it is committed and no longer can be changed
123+-- this is similar to the index in git
124+data DisconnectedTransaction = DisconnectedTransaction UUID DatabaseContext --parentUUID, context
125+
126+transactionUUID :: Transaction -> UUID
127+transactionUUID (Transaction uuid _ _) = uuid
128+
129+transactionContext :: Transaction -> DatabaseContext
130+transactionContext (Transaction _ _ context) = context
131+
132+instance Eq Transaction where
133+ (Transaction uuidA _ _) == (Transaction uuidB _ _) = uuidA == uuidB
134+
135+instance Ord Transaction where
136+ compare (Transaction uuidA _ _) (Transaction uuidB _ _) = compare uuidA uuidB
RelationalError.hs+9−2
@@ -1,9 +1,12 @@
11module RelationalError where
22import RelationType
3+import qualified Data.UUID as U
4+import qualified Data.Set as S
5+
36
47data RelationalError = NoSuchAttributeNameError String
5− | TupleAttributeCountMismatchError Int
6− | TupleAttributeTypeMismatchError Int
8+ | TupleAttributeCountMismatchError Int --attribute name
9+ | TupleAttributeTypeMismatchError Attributes
710 | AttributeCountMismatchError Int
811 | AttributeNameMismatchError String
912 | AttributeNameInUseError AttributeName
@@ -14,6 +17,10 @@ data RelationalError = NoSuchAttributeNameError String
1417 | InclusionDependencyCheckError String
1518 | ParseError String
1619 | PredicateExpressionError String
20+ | NoSuchTransactionError U.UUID
21+ | NoSuchHeadNameError HeadName
22+ | TransactionIsNotAHeadError U.UUID
23+ | TransactionGraphCycleError U.UUID
1724 deriving (Show,Eq)
1825
1926
RelationalTransaction.hsnew file+136
@@ -0,0 +1,136 @@
1+module RelationalTransaction where
2+import qualified Data.UUID as U
3+import RelationType
4+import RelationalError
5+import qualified Data.Set as S
6+import qualified Data.Map as M
7+import Control.Applicative ((<$>))
8+import Control.Monad
9+
10+bootstrapTransactionGraph :: U.UUID -> DatabaseContext -> TransactionGraph
11+bootstrapTransactionGraph freshUUID context = TransactionGraph bootstrapHeads bootstrapTransactions
12+ where
13+ bootstrapHeads = M.singleton "master" freshTransaction
14+ freshTransaction = Transaction freshUUID (TransactionInfo U.nil S.empty) context
15+ bootstrapTransactions = S.singleton freshTransaction
16+
17+transactionForHead :: String -> TransactionGraph -> Maybe Transaction
18+transactionForHead headName (TransactionGraph heads _) = M.lookup headName heads
19+
20+headNameForTransaction :: Transaction -> TransactionGraph -> Maybe String
21+headNameForTransaction transaction (TransactionGraph heads _) = if M.null matchingTrans then
22+ Nothing
23+ else
24+ Just $ (head . M.keys) matchingTrans
25+ where
26+ matchingTrans = M.filter (transaction ==) heads
27+
28+transactionForUUID :: U.UUID -> TransactionGraph -> Either RelationalError Transaction
29+transactionForUUID uuid graph = if S.null matchingTrans then
30+ Left $ NoSuchTransactionError uuid
31+ else
32+ Right $ head (S.toList matchingTrans)
33+ where
34+ matchingTrans = S.filter (\(Transaction uuidMatch _ _) -> uuidMatch == uuid) (transactionsForGraph graph)
35+
36+transactionsForUUIDs :: S.Set U.UUID -> TransactionGraph -> Either RelationalError (S.Set Transaction)
37+transactionsForUUIDs uuidSet graph = do
38+ transList <- forM (S.toList uuidSet) ((flip transactionForUUID) graph)
39+ return (S.fromList transList)
40+
41+isRootTransaction :: Transaction -> TransactionGraph -> Bool
42+isRootTransaction (Transaction _ (TransactionInfo pUUID _) _) graph = U.null pUUID
43+
44+-- the first transaction has no parent - all other do have parents- merges have two parents
45+parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
46+parentTransactions (Transaction _ (TransactionInfo pUUID _) _) graph = do
47+ trans <- transactionForUUID pUUID graph
48+ return (S.singleton trans)
49+
50+parentTransactions (Transaction _ (MergeTransactionInfo pUUID1 pUUID2 _) _ ) graph = transactionsForUUIDs (S.fromList [pUUID1, pUUID2]) graph
51+
52+
53+childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
54+childTransactions (Transaction _ (TransactionInfo _ children) _) = transactionsForUUIDs children
55+childTransactions (Transaction _ (MergeTransactionInfo _ _ children) _) = transactionsForUUIDs children
56+
57+
58+--create a disconnected transaction with a parent
59+newDisconnectedTransaction :: U.UUID -> DatabaseContext -> DisconnectedTransaction
60+newDisconnectedTransaction parentUUID context = DisconnectedTransaction parentUUID context
61+
62+{-
63+addBranch :: HeadName -> Transaction -> TransactionGraph -> Maybe TransactionGraph
64+addBranch newBranchName branchPoint graph@(TransactionGraph heads transSet) = if not (S.member branchPoint transSet) then Nothing else Just $ TransactionGraph newHeads transSet
65+ where
66+ newHeads = M.insert newBranchName branchPoint heads
67+-}
68+
69+-- create a new commit and add it to the heads
70+-- technically, the new head could be added to an existing commit, but by adding a new commit, the new head is unambiguously linked to a new commit (with a context indentical to its parent)
71+addBranch :: U.UUID -> HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
72+addBranch newUUID newBranchName branchPoint graph@(TransactionGraph heads transSet) = addTransactionToGraph newBranchName branchPoint newUUID (transactionContext branchPoint) graph
73+
74+--adds a disconnected transaction to a transaction graph at some head
75+addDisconnectedTransaction :: U.UUID -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
76+addDisconnectedTransaction newUUID headName dTrans@(DisconnectedTransaction parentUUID context) graph = do
77+ parentTrans <- transactionForUUID parentUUID graph
78+ addTransactionToGraph headName parentTrans newUUID context graph
79+
80+-- create a new transaction on "newHeadName" with the branchPointTrans
81+addTransactionToGraph :: HeadName -> Transaction -> U.UUID -> DatabaseContext -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
82+addTransactionToGraph newHeadName branchPointTrans@(Transaction parentUUID _ parentInfo) newUUID newContext graph@(TransactionGraph heads transSet) = do
83+ let freshTransaction = Transaction newUUID (TransactionInfo (transactionUUID branchPointTrans) S.empty) newContext
84+ -- there are two parents for MergeTransactions! not implemented
85+ --update parentTransaction to add child
86+ let parentTransaction = branchPointTrans
87+ let updatedTransSet = (S.insert freshTransaction <$> S.insert parentTransaction <$> S.delete parentTransaction) transSet
88+ let updatedGraph = TransactionGraph (M.insert newHeadName freshTransaction heads) updatedTransSet
89+ return (freshTransaction, updatedGraph)
90+
91+validateGraph :: TransactionGraph -> Maybe [RelationalError]
92+validateGraph graph@(TransactionGraph heads transSet) = do
93+ --check that all UUIDs are unique in the graph
94+ --uuids = map transactionUUID transSet
95+ --check that all heads appear in the transSet
96+ --check that all forward and backward links are in place
97+ errors <- mapM (walkParentTransactions S.empty graph) (S.toList transSet)
98+ mapM (walkChildTransactions S.empty graph) (S.toList transSet)
99+
100+--verify that all parent links exist and that all children exist
101+--maybe verify that all parents end at UUID nil and all children end at leaves
102+walkParentTransactions :: S.Set U.UUID -> TransactionGraph -> Transaction -> Maybe RelationalError
103+walkParentTransactions seenTransSet graph trans =
104+ let transUUID = transactionUUID trans in
105+ if transUUID == U.nil then
106+ Nothing
107+ else if S.member transUUID seenTransSet then
108+ Just $ TransactionGraphCycleError transUUID
109+ else
110+ let parentTransSetOrError = parentTransactions trans graph in
111+ case parentTransSetOrError of
112+ Left err -> Just err
113+ Right parentTransSet -> do
114+ walk <- mapM (walkParentTransactions (S.insert transUUID seenTransSet) graph) (S.toList parentTransSet)
115+ case walk of
116+ err:xs -> Just err
117+ _ -> Nothing
118+
119+--refactor: needless duplication in these two functions
120+walkChildTransactions :: S.Set U.UUID -> TransactionGraph -> Transaction -> Maybe RelationalError
121+walkChildTransactions seenTransSet graph trans =
122+ let transUUID = transactionUUID trans in
123+ if childTransactions trans graph == Right S.empty then
124+ Nothing
125+ else if S.member transUUID seenTransSet then
126+ Just $ TransactionGraphCycleError transUUID
127+ else
128+ let childTransSetOrError = childTransactions trans graph in
129+ case childTransSetOrError of
130+ Left err -> Just err
131+ Right childTransSet -> do
132+ walk <- mapM (walkChildTransactions (S.insert transUUID seenTransSet) graph) (S.toList childTransSet)
133+ case walk of
134+ err:xs -> Just err
135+ _ -> Nothing
136+
RelationalTransactionShow.hsnew file+62
@@ -0,0 +1,62 @@
1+module RelationalTransactionShow where
2+import RelationType
3+import Relation
4+import RelationTuple
5+import RelationTupleSet
6+import RelationalError
7+import RelationalTransaction
8+import qualified Data.Map as M
9+import qualified Data.HashSet as HS
10+import qualified Data.Set as S
11+
12+showTransactionStructure :: Transaction -> TransactionGraph -> String
13+showTransactionStructure trans graph = headInfo ++ " " ++ show (transactionUUID trans) ++ " " ++ parentTransactionsInfo
14+ where
15+ headInfo = maybe "" show (headNameForTransaction trans graph)
16+ parentTransactionsInfo = if isRootTransaction trans graph then "root" else case parentTransactions trans graph of
17+ Left err -> show err
18+ Right parentTransSet -> concat $ S.toList $ S.map (show . transactionUUID) parentTransSet
19+
20+showGraphStructure :: TransactionGraph -> String
21+showGraphStructure graph@(TransactionGraph heads transSet) = S.foldr folder "" transSet
22+ where
23+ folder trans acc = acc ++ showTransactionStructure trans graph ++ "\n"
24+
25+--present a transaction graph as a relation showing the uuids, parentuuids, and flag for the current location of the disconnected transaction
26+graphAsRelation :: DisconnectedTransaction -> TransactionGraph -> Either RelationalError Relation
27+graphAsRelation discon@(DisconnectedTransaction parentUUID _) graph@(TransactionGraph heads transSet) = do
28+ tupleSet <- mapM tupleGenerator (S.toList transSet)
29+ mkRelation attributes (HS.fromList tupleSet)
30+ where
31+ attributes = M.fromList [("id", Attribute "id" StringAtomType),
32+ ("parents", Attribute "parents" (RelationAtomType parentAttributes)),
33+ ("current", Attribute "current" IntAtomType),
34+ ("head", Attribute "head" StringAtomType)
35+ ]
36+ parentAttributes = M.fromList [("id", Attribute "id" StringAtomType)]
37+ tupleGenerator transaction = case transactionParentsRelation transaction graph of
38+ Left err -> Left err
39+ Right parentTransRel -> Right $ mkRelationTuple (S.fromList ["id", "parents", "current", "head"]) $
40+ (M.fromList
41+ [("id", StringAtom $ show (transactionUUID transaction)),
42+ ("parents", RelationAtom parentTransRel),
43+ ("current", IntAtom $ if parentUUID == transactionUUID transaction then 1 else 0),
44+ ("head", StringAtom $ case headNameForTransaction transaction graph of
45+ Just headName -> headName
46+ Nothing -> "")
47+ ])
48+
49+transactionParentsRelation :: Transaction -> TransactionGraph -> Either RelationalError Relation
50+transactionParentsRelation trans graph = do
51+ if isRootTransaction trans graph then do
52+ mkRelation attributes emptyTupleSet
53+ else do
54+ parentTransSet <- parentTransactions trans graph
55+ let tupleSet = HS.fromList $ map trans2tuple (S.toList parentTransSet)
56+ mkRelation attributes tupleSet
57+ where
58+ attributes = M.fromList [("id", Attribute "id" StringAtomType)]
59+ trans2tuple trans = mkRelationTuple (S.fromList ["id"]) $ M.singleton "id" (StringAtom (show $ transactionUUID trans))
60+
61+
62+
TutorialDInterpreter.hs+224−62
@@ -8,6 +8,8 @@ import RelationalError
88import RelationExpr
99import RelationTerm
1010import RelationStaticOptimizer
11+import RelationalTransaction
12+import RelationalTransactionShow
1113import Text.Parsec
1214import Text.Parsec.String
1315import Text.Parsec.Expr
@@ -22,7 +24,24 @@ import Control.Monad.State
2224import System.Console.Haskeline
2325import System.IO
2426import System.Directory (getHomeDirectory)
27+import qualified Data.UUID as U
28+import Data.UUID.V4 (nextRandom)
2529
30+{-
31+List of Tutorial D operators:
32+show relation expression type :: relexpr -> context -> opresult
33+show relvar :: relvar name -> context -> opresult
34+quit :: ()
35+show graph :: graph -> DisconnectedTransaction -> opresult
36+show constraints :: context -> opresult
37+jump to head :: head name -> graph -> DisconnectedTransaction (graph unchanged)
38+jump to UUID :: uuid -> graph -> DisconnectedTransaction (graph unchanged)
39+branch :: uuid -> branch name -> graph -> (DisconnectedTransation, graph)
40+commit :: uuid -> DisconnectedTransaction -> graph -> (DisconnectedTransaction, graph)
41+rollback :: DisconnectedTransaction -> graph -> (DisconnectedTransaction, graph)
42+
43+
44+-}
2645lexer :: Token.TokenParser ()
2746lexer = Token.makeTokenParser tutD
2847 where tutD = emptyDef {
@@ -39,6 +58,13 @@ identifier = Token.identifier lexer
3958comma = Token.comma lexer
4059semi = Token.semi lexer
4160
61+uuidP :: Parser U.UUID
62+uuidP = do
63+ uuidStr <- many (alphaNum <|> char '-')
64+ case U.fromString uuidStr of
65+ Nothing -> fail "Invalid uuid string"
66+ Just uuid -> return uuid
67+
4268--used in projection
4369attributeList :: Parser [AttributeName]
4470attributeList = sepBy identifier comma
@@ -244,49 +270,103 @@ parseString str = case parse (multipleDatabaseExpr <* eof) "" str of
244270 Left err -> Left $ ParseError (show err)
245271 Right r -> Right r
246272
247−data TutorialDOperator where
248− ShowRelation :: RelationalExpr -> TutorialDOperator
249− ShowRelationType :: RelationalExpr -> TutorialDOperator
250− ShowConstraint :: String -> TutorialDOperator
251− ShowPlan :: DatabaseExpr -> TutorialDOperator
252− Quit :: TutorialDOperator
273+--operators which only rely on database context reading
274+data ContextOperator where
275+ ShowRelation :: RelationalExpr -> ContextOperator
276+ ShowRelationType :: RelationalExpr -> ContextOperator
277+ ShowConstraint :: String -> ContextOperator
278+ ShowPlan :: DatabaseExpr -> ContextOperator
279+ Quit :: ContextOperator
253280 deriving (Show)
281+
282+--operators which manipulate a transaction graph
283+data GraphOperator where
284+ JumpToHead :: String -> GraphOperator
285+ JumpToTransaction :: U.UUID -> GraphOperator
286+ Branch :: HeadName -> GraphOperator
287+ Commit :: GraphOperator
288+ Rollback :: GraphOperator
289+ ShowGraph :: GraphOperator
254290
255−typeP :: Parser TutorialDOperator
291+typeP :: Parser ContextOperator
256292typeP = do
257− reservedOp ":t"
293+ reservedOp ":type"
258294 expr <- relExpr
259295 return $ ShowRelationType expr
260296
261−showRelP :: Parser TutorialDOperator
297+showRelP :: Parser ContextOperator
262298showRelP = do
263− reservedOp ":s"
264− expr <- relExpr <* eof
299+ reservedOp ":showExpr"
300+ expr <- relExpr
265301 return $ ShowRelation expr
266302
267−showPlanP :: Parser TutorialDOperator
303+showPlanP :: Parser ContextOperator
268304showPlanP = do
269− reservedOp ":p"
305+ reservedOp ":showplan"
270306 expr <- databaseExpr
271307 return $ ShowPlan expr
272308
273−quitP :: Parser TutorialDOperator
309+quitP :: Parser ContextOperator
274310quitP = do
275− reservedOp ":q"
311+ reservedOp ":quit"
276312 return Quit
277313
278−showConstraintsP :: Parser TutorialDOperator
314+showConstraintsP :: Parser ContextOperator
279315showConstraintsP = do
280− reservedOp ":c"
316+ reservedOp ":constraints"
281317 constraintName <- option "" identifier
282318 return $ ShowConstraint constraintName
283319
284−interpreterOps :: Parser TutorialDOperator
285−interpreterOps = typeP
286− <|> showRelP
287− <|> showConstraintsP
288− <|> showPlanP
289− <|> quitP
320+jumpToHeadP :: Parser GraphOperator
321+jumpToHeadP = do
322+ reservedOp ":jumphead"
323+ head <- identifier
324+ return $ JumpToHead head
325+
326+jumpToTransactionP :: Parser GraphOperator
327+jumpToTransactionP = do
328+ reservedOp ":jump"
329+ uuid <- uuidP
330+ return $ JumpToTransaction uuid
331+
332+branchTransactionP :: Parser GraphOperator
333+branchTransactionP = do
334+ reservedOp ":branch"
335+ branchName <- identifier
336+ return $ Branch branchName
337+
338+commitTransactionP :: Parser GraphOperator
339+commitTransactionP = do
340+ reservedOp ":commit"
341+ return $ Commit
342+
343+rollbackTransactionP :: Parser GraphOperator
344+rollbackTransactionP = do
345+ reservedOp ":rollback"
346+ return $ Rollback
347+
348+showGraphP :: Parser GraphOperator
349+showGraphP = do
350+ reservedOp ":showGraph"
351+ return $ ShowGraph
352+
353+transactionGraphOps = do
354+ jumpToHeadP
355+ <|> jumpToTransactionP
356+ <|> branchTransactionP
357+ <|> commitTransactionP
358+ <|> rollbackTransactionP
359+ <|> showGraphP
360+
361+contextOps :: Parser ContextOperator
362+contextOps = typeP
363+ <|> showRelP
364+ <|> showConstraintsP
365+ <|> showPlanP
366+ <|> quitP
367+
368+interpreterOps :: Parser (Either ContextOperator GraphOperator)
369+interpreterOps = liftM Left contextOps <|> liftM Right transactionGraphOps
290370
291371showRelationAttributes :: Relation -> String
292372showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribute attrs) ++ "}"
@@ -300,19 +380,20 @@ showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribu
300380data TutorialDOperatorResult = QuitResult |
301381 DisplayResult String |
302382 DisplayErrorResult String |
303− NoActionResult
383+ QuietSuccessResult |
384+ NoActionResult --refactor to make this dead- there should be only one parser
304385
305−evalTutorialDOp :: DatabaseContext -> TutorialDOperator -> TutorialDOperatorResult
306−evalTutorialDOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
386+evalContextOp :: DatabaseContext -> ContextOperator -> TutorialDOperatorResult
387+evalContextOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
307388 (Right rel, _) -> DisplayResult $ showRelationAttributes rel
308389 (Left err, _) -> DisplayErrorResult $ show err
309390
310−evalTutorialDOp context (ShowRelation expr) = do
391+evalContextOp context (ShowRelation expr) = do
311392 case runState (evalRelationalExpr expr) context of
312393 (Left err, _) -> DisplayErrorResult $ show err
313394 (Right rel, _) -> DisplayResult $ showRelation rel
314395
315−evalTutorialDOp context (ShowConstraint name) = do
396+evalContextOp context (ShowConstraint name) = do
316397 DisplayResult $ show filteredDeps
317398 where
318399 deps = inclusionDependencies context
@@ -320,23 +401,80 @@ evalTutorialDOp context (ShowConstraint name) = do
320401 "" -> deps
321402 name -> HS.filter (\(InclusionDependency n _ _) -> n == name) deps
322403
323−evalTutorialDOp context (ShowPlan dbExpr) = do
404+evalContextOp context (ShowPlan dbExpr) = do
324405 DisplayResult $ show plan
325406 where
326407 plan = evalState (applyStaticDatabaseOptimization dbExpr) context
327408
328−evalTutorialDOp context (Quit) = QuitResult
329−
330−example1 = "relA {a,b, c}"
331−example2 = "relA join relB"
332−example3 = "relA join relB {x,y,z}"
333−example4 = "(relA) {x,y,z}"
334−example5 = "relA union relB"
335−example6 = "rv1 := true"
336−example7 = "rv1 := relA union relB"
337−example8 = "relA := true; relB := false"
338−example9 = "relA := relation { SNO CHAR }"
409+evalContextOp context (Quit) = QuitResult
410+
411+-- returns the new "current" transaction, updated graph, and tutorial d result
412+-- the current transaction is not part of the transaction graph until it is committed
413+evalGraphOp :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> GraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
414+
415+--affects only disconncted transaction
416+evalGraphOp newUUID trans graph (JumpToTransaction jumpUUID) = case transactionForUUID jumpUUID graph of
417+ Left err -> Left err
418+ Right parentTrans -> Right (newTrans, graph, QuietSuccessResult)
419+ where
420+ newTrans = DisconnectedTransaction jumpUUID (transactionContext parentTrans)
339421
422+-- switch from one head to another
423+-- affects only disconnectedtransaction
424+evalGraphOp newUUID currentTransaction graph (JumpToHead headName) =
425+ case transactionForHead headName graph of
426+ Just newHeadTransaction -> let disconnectedTrans = newDisconnectedTransaction (transactionUUID newHeadTransaction) (transactionContext newHeadTransaction) in
427+ Right (disconnectedTrans, graph, QuietSuccessResult)
428+ Nothing -> Left $ NoSuchHeadNameError headName
429+-- add new head pointing to branchPoint
430+-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
431+-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
432+ {-
433+evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
434+ Nothing -> (discon, graph, DisplayErrorResult "Failed to find parent transaction.")
435+ Just parentTrans -> case addBranch newBranchName parentTrans graph of
436+ Nothing -> (discon, graph, DisplayErrorResult "Failed to add branch.")
437+ Just newGraph -> (newDiscon, newGraph, DisplayResult "Branched.")
438+ where
439+ newDiscon = DisconnectedTransaction (transactionUUID parentTrans) disconContext
440+-}
441+
442+-- create a new commit and add it to the heads
443+-- technically, the new head could be added to an existing commit, but by adding a new commit, the new head is unambiguously linked to a new commit (with a context indentical to its parent)
444+evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
445+ Left err -> Left err
446+ Right parentTrans -> case addBranch newUUID newBranchName parentTrans graph of
447+ Left err -> Left err
448+ Right (newTrans, newGraph) -> Right (newDiscon, newGraph, QuietSuccessResult)
449+ where
450+ newDiscon = DisconnectedTransaction newUUID disconContext
451+
452+-- add the disconnected transaction to the graph
453+-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
454+evalGraphOp newTransUUID discon@(DisconnectedTransaction parentUUID context) graph Commit = case transactionForUUID parentUUID graph of
455+ Left err -> Left err
456+ Right parentTransaction -> case headNameForTransaction parentTransaction graph of
457+ Nothing -> Left $ TransactionIsNotAHeadError parentUUID
458+ Just headName -> case maybeUpdatedGraph of
459+ Left err-> Left err
460+ Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph, QuietSuccessResult)
461+ where
462+ newDisconnectedTrans = newDisconnectedTransaction newTransUUID context
463+ maybeUpdatedGraph = addDisconnectedTransaction newTransUUID headName discon graph
464+
465+-- refresh the disconnected transaction, return the same graph
466+evalGraphOp _ discon@(DisconnectedTransaction parentUUID _) graph Rollback = case transactionForUUID parentUUID graph of
467+ Left err -> Left err
468+ Right parentTransaction -> Right (newDiscon, graph, QuietSuccessResult)
469+ where
470+ newDiscon = newDisconnectedTransaction parentUUID (transactionContext parentTransaction)
471+
472+--display transaction graph as relation
473+evalGraphOp _ discon graph ShowGraph = do
474+ graphStr <- graphAsRelation discon graph
475+ return (discon, graph, DisplayResult $ showRelation graphStr)
476+
477+--shouldn't this be Either RelationalError DatabaseContext?
340478interpret :: DatabaseContext -> String -> (Maybe RelationalError, DatabaseContext)
341479interpret context tutdstring = case parseString tutdstring of
342480 Left err -> (Just err, context)
@@ -358,33 +496,53 @@ interpretNO context tutdstring = case parseString tutdstring of
358496
359497
360498-- for interpreter-specific operations
361−interpretOps :: DatabaseContext -> String -> TutorialDOperatorResult
362−interpretOps context instring = case parse interpreterOps "" instring of
363− Left err -> NoActionResult
364− Right ops -> evalTutorialDOp context ops
499+interpretOps :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> String -> (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
500+interpretOps newUUID trans@(DisconnectedTransaction parentUUID context) transGraph instring = case parse interpreterOps "" instring of
501+ Left err -> (trans, transGraph, NoActionResult)
502+ Right ops -> case ops of
503+ Left contextOp -> (trans, transGraph, (evalContextOp context contextOp))
504+ Right graphOp -> case evalGraphOp newUUID trans transGraph graphOp of
505+ Left err -> (trans, transGraph, DisplayErrorResult $ show err)
506+ Right (newDiscon, newGraph, result) -> (newDiscon, newGraph, result)
365507
366−reprLoop :: DatabaseContext -> IO ()
367−reprLoop context = do
508+promptText :: DisconnectedTransaction -> TransactionGraph -> String
509+promptText (DisconnectedTransaction parentUUID _) graph = "TutorialD (" ++ transInfo ++ "): "
510+ where
511+ transInfo = case transactionForUUID parentUUID graph of
512+ Left err -> "unknown"
513+ Right parentTrans -> case headNameForTransaction parentTrans graph of
514+ Nothing -> show $ transactionUUID parentTrans
515+ Just headName -> headName
516+
517+reprLoop :: DisconnectedTransaction -> TransactionGraph -> IO ()
518+reprLoop currentTransaction@(DisconnectedTransaction currentParentUUID currentContext) graph = do
368519 homeDirectory <- getHomeDirectory
369− let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
370− maybeLine <- runInputT settings $ do
371− getInputLine "TutorialD: "
520+ let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
521+
522+ maybeLine <- runInputT settings $ getInputLine (promptText currentTransaction graph)
523+
524+ let roloop = reprLoop currentTransaction graph
372525 case maybeLine of
373526 Nothing -> return ()
374527 Just line -> do
375− case interpretOps context line of
376− QuitResult -> return ()
377− DisplayErrorResult err -> hPutStrLn stderr ("ERR: " ++ err) >> reprLoop context
378− DisplayResult out -> do
528+ newUUID <- nextRandom
529+ case interpretOps newUUID currentTransaction graph line of
530+ (_, _, QuitResult) -> return ()
531+ (_, _, DisplayErrorResult err) -> hPutStrLn stderr ("ERR: " ++ err) >> roloop
532+ (updatedTrans, updatedGraph, DisplayResult out) -> do
379533 putStrLn out
380− reprLoop context
381− NoActionResult -> do
382− let (value, contextup) = interpret context line
383− case value of
384− Nothing -> reprLoop contextup
385− (Just err) -> do
386− hPutStrLn stderr ("ERR:" ++ show err)
387− reprLoop context
534+ reprLoop updatedTrans updatedGraph
535+ (updatedTrans, updatedGraph, QuietSuccessResult) -> do
536+ putStrLn "Done."
537+ reprLoop updatedTrans updatedGraph
538+ (_, _, NoActionResult) -> do
539+ let (value, contextup) = interpret currentContext line
540+ let updatedTransaction = DisconnectedTransaction currentParentUUID contextup
541+ case value of
542+ Nothing -> reprLoop updatedTransaction graph
543+ Just err -> do
544+ hPutStrLn stderr ("ERR:" ++ show err)
545+ reprLoop currentTransaction graph
388546
389547restrictionPredicateP :: Parser RestrictionPredicateExpr
390548restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
@@ -419,4 +577,8 @@ stringAtomP = liftA StringAtom (string "\"" *> many quotedChar <* string "\"")
419577intAtomP = do
420578 intstr <- many digit
421579 return $ IntAtom (read intstr)
422−
580+
581+--used by :dumpGraph
582+displayTransactionGraph :: TransactionGraph -> String
583+displayTransactionGraph (TransactionGraph heads transSet) = L.intercalate "\n" $ S.foldr (\(Transaction tUUID pUUID _ ) acc -> acc ++ [show tUUID ++ " " ++ show pUUID]) [] transSet
584+
TutorialDInterpreterTest.hs+98−2
@@ -4,15 +4,20 @@ import RelationExpr
44import Relation
55import RelationalError
66import RelationType
7+import RelationalTransaction
78import qualified Data.HashSet as HS
89import qualified Data.Map as M
10+import qualified Data.Set as S
911import System.Exit
12+import Data.UUID.V4 (nextRandom)
13+import Data.UUID (nil)
14+import Data.Either (isRight)
1015
1116main = do
1217 counts <- runTestTT (TestList tests)
1318 if errors counts + failures counts > 0 then exitFailure else exitSuccess
1419 where
15− tests = map (\(tutd, expected) -> TestCase $ assertTutdEqual basicDatabaseContext expected tutd) simpleRelTests ++ map (\(tutd, expected) -> TestCase $ assertTutdEqual dateExamples expected tutd) dateExampleRelTests
20+ tests = map (\(tutd, expected) -> TestCase $ assertTutdEqual basicDatabaseContext expected tutd) simpleRelTests ++ map (\(tutd, expected) -> TestCase $ assertTutdEqual dateExamples expected tutd) dateExampleRelTests ++ [transactionGraphBasicTest, transactionGraphAddCommitTest, transactionRollbackTest, transactionJumpTest, transactionBranchTest]
1621 simpleRelTests = [("x:=true", Right relationTrue),
1722 ("x:=false", Right relationFalse),
1823 ("x:=true union false", Right relationTrue),
@@ -52,4 +57,95 @@ assertTutdEqual databaseContext expected tutd = assertEqual tutd expected interp
5257 (Just err, _) -> Left err
5358 (Nothing, context) -> case M.lookup "x" (relationVariables context) of
5459 Nothing -> Left $ RelVarNotDefinedError "x"
55− Just rel -> Right rel
60+ Just rel -> Right rel
61+
62+transactionGraphBasicTest = TestCase $ do
63+ (discon, graph) <- dateExamplesGraph
64+ assertEqual "validate bootstrapped graph" (validateGraph graph) Nothing
65+
66+--add a new transaction to the graph, validate it is in the graph
67+transactionGraphAddCommitTest = TestCase $ do
68+ (discon@(DisconnectedTransaction firstUUID context), graph) <- dateExamplesGraph
69+ freshUUID <- nextRandom
70+ case interpret context "x:=S" of
71+ (Just err, newContext) -> assertFailure (show err)
72+ (Nothing, newContext) -> do
73+ let discon = newDisconnectedTransaction firstUUID newContext
74+ let addTrans = addDisconnectedTransaction freshUUID "master" discon graph
75+ case addTrans of
76+ Left err -> assertFailure (show err)
77+ Right (newTrans, graph) -> do
78+ assertBool "transaction in graph" (isRight $ transactionForUUID freshUUID graph)
79+ assertEqual "validate fresh commit with deleted S" (validateGraph graph) Nothing
80+ assertEqual "ensure S was deleted in newContext" (M.lookup "x" (relationVariables (transactionContext newTrans))) (Just s)
81+
82+transactionRollbackTest = TestCase $ do
83+ (origDiscon@(DisconnectedTransaction firstUUID origContext), graph) <- dateExamplesGraph
84+ freshUUID <- nextRandom
85+ case transactionForUUID firstUUID graph of
86+ Left err -> assertFailure (show err)
87+ Right firstTrans -> do
88+ case interpret origContext "x:=S" of
89+ (Just err, _) -> assertFailure (show err)
90+ (Nothing, newContext) -> do
91+ let discon = newDisconnectedTransaction firstUUID newContext
92+ let (updatedDiscon@(DisconnectedTransaction _ newContext2), newGraph, result) = interpretOps freshUUID discon graph ":rollback"
93+ assertEqual "validate context" (M.lookup "x" (relationVariables newContext2)) Nothing
94+ assertEqual "validate graph" graph newGraph
95+
96+--commit a new transaction with "x" relation, jump to first transaction, verify that "x" is not present
97+transactionJumpTest = TestCase $ do
98+ (origDiscon@(DisconnectedTransaction firstUUID origContext), graph) <- dateExamplesGraph
99+ freshUUID <- nextRandom
100+ case transactionForUUID firstUUID graph of
101+ Left err -> assertFailure (show err)
102+ Right firstTrans -> do
103+ case interpret origContext "x:=S" of
104+ (Just err, _) -> assertFailure (show err)
105+ (Nothing, newContext) -> do
106+ --modify the second transaction
107+ case interpret newContext "x:=S" of
108+ (Just err, _) -> assertFailure (show err)
109+ (Nothing, newContext2) -> do
110+ --add the transaction
111+ let discon = newDisconnectedTransaction firstUUID newContext2
112+ let addTrans = addDisconnectedTransaction freshUUID "master" discon graph
113+ case addTrans of
114+ Left err -> assertFailure (show err)
115+ Right (newTrans, graph) -> do
116+ --jump to the first transaction
117+ let (updatedDiscon@(DisconnectedTransaction parentUUID newContext3), newGraph, result) = interpretOps freshUUID discon graph (":jump " ++ show firstUUID)
118+ assertEqual "validate discon" parentUUID firstUUID
119+ assertEqual "validate discon2" Nothing (M.lookup "x" (relationVariables newContext3))
120+ assertEqual "validate graph" transactionUUIDs (S.map transactionUUID (transactionsForGraph newGraph))
121+ where
122+ transactionUUIDs = S.fromList [firstUUID, freshUUID]
123+
124+--branch from the first transaction and verify that there are two heads
125+transactionBranchTest = TestCase $ do
126+ (origDiscon@(DisconnectedTransaction firstUUID origContext), graph) <- dateExamplesGraph
127+ freshUUID1 <- nextRandom
128+ freshUUID2 <- nextRandom
129+ case transactionForUUID firstUUID graph of
130+ Left err -> assertFailure (show err)
131+ Right firstTrans -> do
132+ let disconMaster = newDisconnectedTransaction firstUUID origContext
133+ let addTransMaster = addDisconnectedTransaction freshUUID1 "master" disconMaster graph
134+ --add a second transaction to the "master" branch
135+ case addTransMaster of
136+ Left err -> assertFailure (show err)
137+ Right (newTrans, graph) -> do
138+ --add a third transaction to the "test" branch
139+ let (updatedDiscon, newGraph, result) = interpretOps freshUUID2 origDiscon graph ":branch test"
140+ assertEqual "verify test head" freshUUID2 $ maybeTransUUID (transactionForHead "test" newGraph)
141+ assertEqual "verify master head" freshUUID1 $ maybeTransUUID (transactionForHead "master" newGraph)
142+ where
143+ maybeTransUUID t = maybe nil transactionUUID t
144+
145+dateExamplesGraph :: IO (DisconnectedTransaction, TransactionGraph)
146+dateExamplesGraph = do
147+ firstTransactionUUID <- nextRandom
148+ let graph = bootstrapTransactionGraph firstTransactionUUID dateExamples
149+ let discon = newDisconnectedTransaction firstTransactionUUID dateExamples
150+ return (discon, graph)
151+
project-m36.cabal+1−1
@@ -7,7 +7,7 @@ Category: Relational Algebra
77Cabal-Version: >= 1.2
88
99Executable tutd
10− Build-Depends: base,mtl,containers,unordered-containers,parsec,hashable,haskeline,directory
10+ Build-Depends: base,mtl,containers,unordered-containers,parsec,hashable,haskeline,directory,uuid
1111 main-is: tutd.hs
1212 GHC-Options: -fwarn-unused-imports -fwarn-unused-binds -fwarn-incomplete-patterns -fwarn-incomplete-uni-patterns
1313
tutd.hs+11−1
@@ -1,4 +1,14 @@
11import TutorialDInterpreter
22import RelationExpr
3+import RelationalTransaction
4+import RelationType
5+import Data.UUID.V4 (nextRandom)
6+
7+main = do
8+ freshUUID <- nextRandom
9+ let currentHeadName = "master"
10+ let newGraph = bootstrapTransactionGraph freshUUID dateExamples
11+ let headTransaction = case transactionForHead currentHeadName newGraph of { Just x -> x; _ -> undefined}
12+ let currentTransaction = newDisconnectedTransaction (transactionUUID headTransaction) (transactionContext headTransaction)
13+ reprLoop currentTransaction newGraph
314
4−main = reprLoop dateExamples