@@ -5,6 +5,7 @@ import RelationalError
5import qualified Data.Set as S
5import qualified Data.Set as S
6import qualified Data.Map as M
6import qualified Data.Map as M
7import Control.Applicative ((<$>))
7import Control.Applicative ((<$>))
9bootstrapTransactionGraph :: UUID -> DatabaseContext -> TransactionGraph
10bootstrapTransactionGraph :: UUID -> DatabaseContext -> TransactionGraph
10bootstrapTransactionGraph freshUUID context = TransactionGraph bootstrapHeads bootstrapTransactions
11bootstrapTransactionGraph freshUUID context = TransactionGraph bootstrapHeads bootstrapTransactions
@@ -31,21 +32,25 @@ transactionForUUID uuid graph = if S.null matchingTrans then
31 Right $ head (S.toList matchingTrans)
32 Right $ head (S.toList matchingTrans)
33 matchingTrans = S.filter (\(Transaction uuidMatch _ _) -> uuidMatch == uuid) (transactionsForGraph graph)
34 matchingTrans = S.filter (\(Transaction uuidMatch _ _) -> uuidMatch == uuid) (transactionsForGraph graph)
35transactionsForUUIDs :: S.Set UUID -> TransactionGraph -> Either RelationalError (S.Set Transaction)
36transactionsForUUIDs :: S.Set UUID -> TransactionGraph -> Either RelationalError (S.Set Transaction)
36−transactionsForUUIDs uuidSet graph = S.map transactionForUUID uuidSet
37+transactionsForUUIDs uuidSet graph = do
38+ transList <- forM (S.toList uuidSet) ((flip transactionForUUID) graph)
39+ return (S.fromList transList)
38-- the first transaction has no parent - all other do have parents- merges have two parents
42-- the first transaction has no parent - all other do have parents- merges have two parents
39parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
43parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
40−parentTransactions (Transaction _ (TransactionInfo pUUID _)) = case transactionForUUID pUUID of
44+parentTransactions (Transaction _ (TransactionInfo pUUID _) _) graph = do
45+ trans <- transactionForUUID pUUID graph
41− Just trans -> S.singleton trans
46+ return (S.singleton trans)
42− Nothing -> S.empty
47+
43−parentTransactions (Transaction _ (MergeTransactionInfo pUUID1 pUUID2 _)) = transactionsForUUIDs (S.fromList [pUUID1 pUUID2])
48+parentTransactions (Transaction _ (MergeTransactionInfo pUUID1 pUUID2 _) _ ) graph = transactionsForUUIDs (S.fromList [pUUID1, pUUID2]) graph
46−childTransactions :: Transaction -> TransactionGraph -> S.Set Transaction
51+childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
47−childTransactions (Transaction _ _ (TransactionInfo _ children _)) = undefined
52+childTransactions (Transaction _ (TransactionInfo _ children) _) = transactionsForUUIDs children
48−childTransactions (Transaction _ _ (MergeTransactionInfo _ _ children _)) = undefined
53+childTransactions (Transaction _ (MergeTransactionInfo _ _ children) _) = transactionsForUUIDs children
51--create a disconnected transaction with a parent
56--create a disconnected transaction with a parent
@@ -61,20 +66,19 @@ addBranch newBranchName branchPoint graph@(TransactionGraph heads transSet) = if
62-- create a new commit and add it to the heads
67-- create a new commit and add it to the heads
63-- 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)
68-- 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)
64−addBranch :: UUID -> HeadName -> Transaction -> TransactionGraph -> Maybe (Transaction, TransactionGraph)
69+addBranch :: UUID -> HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
65addBranch newUUID newBranchName branchPoint graph@(TransactionGraph heads transSet) = addTransactionToGraph newBranchName branchPoint newUUID (transactionContext branchPoint) graph
70addBranch newUUID newBranchName branchPoint graph@(TransactionGraph heads transSet) = addTransactionToGraph newBranchName branchPoint newUUID (transactionContext branchPoint) graph
67--adds a disconnected transaction to a transaction graph at some head
72--adds a disconnected transaction to a transaction graph at some head
68−addDisconnectedTransaction :: UUID -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Maybe (Transaction, TransactionGraph)
73+addDisconnectedTransaction :: UUID -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
69−addDisconnectedTransaction newUUID headName dTrans@(DisconnectedTransaction parentUUID context) graph = case transactionForUUID parentUUID graph of
74+addDisconnectedTransaction newUUID headName dTrans@(DisconnectedTransaction parentUUID context) graph = do
70− Nothing -> Nothing
75+ parentTrans <- transactionForUUID parentUUID graph
72− addTransactionToGraph headName parentTrans newUUID context graph
76+ addTransactionToGraph headName parentTrans newUUID context graph
74-- create a new transaction on "newHeadName" with the branchPointTrans
78-- create a new transaction on "newHeadName" with the branchPointTrans
75−addTransactionToGraph :: HeadName -> Transaction -> UUID -> DatabaseContext -> TransactionGraph -> Maybe (Transaction, TransactionGraph)
79+addTransactionToGraph :: HeadName -> Transaction -> UUID -> DatabaseContext -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
76addTransactionToGraph newHeadName branchPointTrans@(Transaction parentUUID _ parentInfo) newUUID newContext graph@(TransactionGraph heads transSet) = do
80addTransactionToGraph newHeadName branchPointTrans@(Transaction parentUUID _ parentInfo) newUUID newContext graph@(TransactionGraph heads transSet) = do
77− let freshTransaction = Transaction newUUID (transactionUUID branchPointTrans) (TransactionInfo S.empty newContext)
81+ let freshTransaction = Transaction newUUID (TransactionInfo (transactionUUID branchPointTrans) S.empty) newContext
78 -- there are two parents for MergeTransactions! not implemented
82 -- there are two parents for MergeTransactions! not implemented
79 --update parentTransaction to add child
83 --update parentTransaction to add child
80 let parentTransaction = branchPointTrans
84 let parentTransaction = branchPointTrans
@@ -83,8 +87,45 @@ addTransactionToGraph newHeadName branchPointTrans@(Transaction parentUUID _ par
83 return (freshTransaction, updatedGraph)
87 return (freshTransaction, updatedGraph)
85--check that all forward and backward links are in place
89--check that all forward and backward links are in place
86−validateGraph :: TransactionGraph -> Maybe RelationalError
90+validateGraph :: TransactionGraph -> Maybe [RelationalError]
87−validateGraph (TransactionGraph heads transSet) = undefined
91+validateGraph graph@(TransactionGraph heads transSet) = do
92+ errors <- mapM (walkParentTransactions S.empty graph) (S.toList transSet)
93+ mapM (walkChildTransactions S.empty graph) (S.toList transSet)
95+--verify that all parent links exist and that all children exist
96+--maybe verify that all parents end at UUID nil and all children end at leaves
97+walkParentTransactions :: S.Set UUID -> TransactionGraph -> Transaction -> Maybe RelationalError
98+walkParentTransactions seenTransSet graph trans =
99+ let transUUID = transactionUUID trans in
100+ if transUUID == nil then
102+ else if S.member transUUID seenTransSet then
103+ Just $ TransactionGraphCycleError transUUID
105+ let parentTransSetOrError = parentTransactions trans graph in
106+ case parentTransSetOrError of
107+ Left err -> Just err
108+ Right parentTransSet -> do
109+ walk <- mapM (walkParentTransactions (S.insert transUUID seenTransSet) graph) (S.toList parentTransSet)
114+--refactor: needless duplication in these two functions
89−validateTransaction :: Transaction -> TransactionGraph -> Maybe RelationalError
115+walkChildTransactions :: S.Set UUID -> TransactionGraph -> Transaction -> Maybe RelationalError
90−validateTransaction trans@(Transaction sUUID pUUID info) = undefined
116+walkChildTransactions seenTransSet graph trans =
117+ let transUUID = transactionUUID trans in
118+ if childTransactions trans graph == Right S.empty then
120+ else if S.member transUUID seenTransSet then
121+ Just $ TransactionGraphCycleError transUUID
123+ let childTransSetOrError = childTransactions trans graph in
124+ case childTransSetOrError of
125+ Left err -> Just err
126+ Right childTransSet -> do
127+ walk <- mapM (walkChildTransactions (S.insert transUUID seenTransSet) graph) (S.toList childTransSet)