@@ -2,11 +2,28 @@
22module ProjectM36.TransactionGraph where
33import ProjectM36.Base
44import ProjectM36.Error
5+import ProjectM36.Transaction
6+import ProjectM36.Relation
7+import ProjectM36.TupleSet
8+import ProjectM36.Tuple
9+import qualified Data.HashSet as HS
10+import qualified Data.Vector as V
11+import qualified ProjectM36.Attribute as A
512import qualified Data.UUID as U
613import qualified Data.Set as S
714import qualified Data.Map as M
815import Control.Applicative ((<$>))
916import Control.Monad
17+import qualified Data.Text as T
18+
19+--operators which manipulate a transaction graph
20+data TransactionGraphOperator = JumpToHead HeadName |
21+ JumpToTransaction U.UUID |
22+ Branch HeadName |
23+ Commit |
24+ Rollback
25+
26+data ROTransactionGraphOperator = ShowGraph
1027
1128bootstrapTransactionGraph :: U.UUID -> DatabaseContext -> TransactionGraph
1229bootstrapTransactionGraph freshUUID context = TransactionGraph bootstrapHeads bootstrapTransactions
@@ -126,3 +143,104 @@ walkChildTransactions seenTransSet graph trans =
126143 case walk of
127144 err:_ -> Just err
128145 _ -> Nothing
146+
147+-- returns the new "current" transaction, updated graph, and tutorial d result
148+-- the current transaction is not part of the transaction graph until it is committed
149+evalGraphOp :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> TransactionGraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
150+
151+--affects only disconncted transaction
152+evalGraphOp _ _ graph (JumpToTransaction jumpUUID) = case transactionForUUID jumpUUID graph of
153+ Left err -> Left err
154+ Right parentTrans -> Right (newTrans, graph)
155+ where
156+ newTrans = DisconnectedTransaction jumpUUID (transactionContext parentTrans)
157+
158+-- switch from one head to another
159+-- affects only disconnectedtransaction
160+evalGraphOp _ _ graph (JumpToHead headName) =
161+ case transactionForHead headName graph of
162+ Just newHeadTransaction -> let disconnectedTrans = newDisconnectedTransaction (transactionUUID newHeadTransaction) (transactionContext newHeadTransaction) in
163+ Right (disconnectedTrans, graph)
164+ Nothing -> Left $ NoSuchHeadNameError headName
165+-- add new head pointing to branchPoint
166+-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
167+-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
168+ {-
169+evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
170+ Nothing -> (discon, graph, DisplayErrorResult "Failed to find parent transaction.")
171+ Just parentTrans -> case addBranch newBranchName parentTrans graph of
172+ Nothing -> (discon, graph, DisplayErrorResult "Failed to add branch.")
173+ Just newGraph -> (newDiscon, newGraph, DisplayResult "Branched.")
174+ where
175+ newDiscon = DisconnectedTransaction (transactionUUID parentTrans) disconContext
176+-}
177+
178+-- create a new commit and add it to the heads
179+-- 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)
180+evalGraphOp newUUID (DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
181+ Left err -> Left err
182+ Right parentTrans -> case addBranch newUUID newBranchName parentTrans graph of
183+ Left err -> Left err
184+ Right (_, newGraph) -> Right (newDiscon, newGraph)
185+ where
186+ newDiscon = DisconnectedTransaction newUUID disconContext
187+
188+-- add the disconnected transaction to the graph
189+-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
190+evalGraphOp newTransUUID discon@(DisconnectedTransaction parentUUID context) graph Commit = case transactionForUUID parentUUID graph of
191+ Left err -> Left err
192+ Right parentTransaction -> case headNameForTransaction parentTransaction graph of
193+ Nothing -> Left $ TransactionIsNotAHeadError parentUUID
194+ Just headName -> case maybeUpdatedGraph of
195+ Left err-> Left err
196+ Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
197+ where
198+ newDisconnectedTrans = newDisconnectedTransaction newTransUUID context
199+ maybeUpdatedGraph = addDisconnectedTransaction newTransUUID headName discon graph
200+
201+-- refresh the disconnected transaction, return the same graph
202+evalGraphOp _ (DisconnectedTransaction parentUUID _) graph Rollback = case transactionForUUID parentUUID graph of
203+ Left err -> Left err
204+ Right parentTransaction -> Right (newDiscon, graph)
205+ where
206+ newDiscon = newDisconnectedTransaction parentUUID (transactionContext parentTransaction)
207+
208+--display transaction graph as relation
209+evalROGraphOp :: DisconnectedTransaction -> TransactionGraph -> ROTransactionGraphOperator -> Either RelationalError Relation
210+evalROGraphOp discon graph ShowGraph = do
211+ graphRel <- graphAsRelation discon graph
212+ return graphRel
213+
214+--present a transaction graph as a relation showing the uuids, parentuuids, and flag for the current location of the disconnected transaction
215+graphAsRelation :: DisconnectedTransaction -> TransactionGraph -> Either RelationalError Relation
216+graphAsRelation (DisconnectedTransaction parentUUID _) graph@(TransactionGraph _ transSet) = do
217+ tupleMatrix <- mapM tupleGenerator (S.toList transSet)
218+ mkRelationFromList attrs tupleMatrix
219+ where
220+ attrs = A.attributesFromList [Attribute "id" StringAtomType,
221+ Attribute "parents" (RelationAtomType parentAttributes),
222+ Attribute "current" IntAtomType,
223+ Attribute "head" StringAtomType
224+ ]
225+ parentAttributes = A.attributesFromList [Attribute "id" StringAtomType]
226+ tupleGenerator transaction = case transactionParentsRelation transaction graph of
227+ Left err -> Left err
228+ Right parentTransRel -> Right [StringAtom $ T.pack $ show (transactionUUID transaction),
229+ RelationAtom parentTransRel,
230+ IntAtom $ if parentUUID == transactionUUID transaction then 1 else 0,
231+ StringAtom $ case headNameForTransaction transaction graph of
232+ Just headName -> headName
233+ Nothing -> ""
234+ ]
235+
236+transactionParentsRelation :: Transaction -> TransactionGraph -> Either RelationalError Relation
237+transactionParentsRelation trans graph = do
238+ if isRootTransaction trans graph then do
239+ mkRelation attrs emptyTupleSet
240+ else do
241+ parentTransSet <- parentTransactions trans graph
242+ let tupleSet = HS.fromList $ map trans2tuple (S.toList parentTransSet)
243+ mkRelation attrs tupleSet
244+ where
245+ attrs = A.attributesFromList [Attribute "id" StringAtomType]
246+ trans2tuple trans2 = mkRelationTuple attrs $ V.singleton (StringAtom (T.pack (show $ transactionUUID trans2)))